diff options
549 files changed, 13677 insertions, 4798 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 7e6c30f0719a..28462217e5de 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -75,6 +75,7 @@ aconfig_srcjars = [ ":com.android.internal.pm.pkg.component.flags-aconfig-java{.generated_srcjars}", ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}", ":com.android.media.flags.editing-aconfig-java{.generated_srcjars}", + ":com.android.media.flags.projection-aconfig-java{.generated_srcjars}", ":com.android.net.thread.flags-aconfig-java{.generated_srcjars}", ":com.android.server.flags.services-aconfig-java{.generated_srcjars}", ":com.android.text.flags-aconfig-java{.generated_srcjars}", @@ -566,6 +567,21 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// MediaProjection +aconfig_declarations { + name: "com.android.media.flags.projection-aconfig", + package: "com.android.media.projection.flags", + srcs: [ + "media/java/android/media/flags/projection.aconfig", + ], +} + +java_aconfig_library { + name: "com.android.media.flags.projection-aconfig-java", + aconfig_declarations: "com.android.media.flags.projection-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Media TV aconfig_declarations { name: "android.media.tv.flags-aconfig", diff --git a/Android.bp b/Android.bp index e12f74fcd7ca..870df5a5723e 100644 --- a/Android.bp +++ b/Android.bp @@ -508,6 +508,8 @@ java_library { lint: { baseline_filename: "lint-baseline.xml", }, + // For jarjar repackaging + jarjar_prefix: "com.android.internal.hidden_from_bootclasspath", } java_library { diff --git a/Ravenwood.bp b/Ravenwood.bp index 2babf6a56ab4..633702233cf4 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -93,16 +93,33 @@ java_library { ], } +// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically. +// Rename some of the dependencies to make sure they're included in the intended order. +java_genrule { + name: "100-framework-minus-apex.ravenwood", + cmd: "cp $(in) $(out)", + srcs: [":framework-minus-apex.ravenwood"], + out: ["100-framework-minus-apex.ravenwood.jar"], + visibility: ["//visibility:private"], +} + +java_genrule { + // Use 200 to make sure it comes before the mainline stub ("all-updatable..."). + name: "200-kxml2-android", + cmd: "cp $(in) $(out)", + srcs: [":kxml2-android"], + out: ["200-kxml2-android.jar"], + visibility: ["//visibility:private"], +} + android_ravenwood_libgroup { name: "ravenwood-runtime", libs: [ - // Prefixed with "200" to ensure it's sorted early in Tradefed classpath - // so that we provide a concrete implementation before Mainline stubs + "100-framework-minus-apex.ravenwood", "200-kxml2-android", "all-updatable-modules-system-stubs", "android.test.mock.ravenwood", - "framework-minus-apex.ravenwood", - "hoststubgen-helper-framework-runtime.ravenwood", + "ravenwood-helper-runtime", "hoststubgen-helper-runtime.ravenwood", // Provide runtime versions of utils linked in below diff --git a/WEAR_OWNERS b/WEAR_OWNERS index da8c83ebcc98..4ffb239e24ce 100644 --- a/WEAR_OWNERS +++ b/WEAR_OWNERS @@ -11,3 +11,4 @@ rwmyers@google.com nalmalki@google.com shijianli@google.com latkin@google.com +djsollen@google.com diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index f819f15b430f..bd00c03741f3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -3852,7 +3852,7 @@ public class JobSchedulerService extends com.android.server.SystemService // the other jobs that will use this network. if (DEBUG) { Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: piggybacking " - + batchedJobs.size() + " jobs on " + network + + (batchedJobs.size() - unbatchedJobCount) + " jobs on " + network + " because of unbatched job"); } jobsToRun.addAll(batchedJobs); @@ -3892,8 +3892,12 @@ public class JobSchedulerService extends com.android.server.SystemService // Some job is going to use the CPU anyway. Might as well run all the other // CPU-only jobs. if (DEBUG) { + final Integer unbatchedJobCountObj = mUnbatchedJobCount.get(null); + final int unbatchedJobCount = + unbatchedJobCountObj == null ? 0 : unbatchedJobCountObj; Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: piggybacking " - + batchedNonNetworkedJobs.size() + " non-network jobs"); + + (batchedNonNetworkedJobs.size() - unbatchedJobCount) + + " non-network jobs"); } jobsToRun.addAll(batchedNonNetworkedJobs); } else if (batchedNonNetworkedJobs.size() >= minReadyCount) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 6ed42ec990ea..3219f7e5ce20 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -139,8 +140,9 @@ public final class ConnectivityController extends RestrictingController implemen static final SparseIntArray sNetworkTransportAffinities = new SparseIntArray(); static { sNetworkTransportAffinities.put(TRANSPORT_CELLULAR, TRANSPORT_AFFINITY_AVOID); - sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER); sNetworkTransportAffinities.put(TRANSPORT_ETHERNET, TRANSPORT_AFFINITY_PREFER); + sNetworkTransportAffinities.put(TRANSPORT_SATELLITE, TRANSPORT_AFFINITY_AVOID); + sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER); } private final CcConfig mCcConfig; diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt index 26dc7003a828..adcc3df2d7fe 100644 --- a/boot/hiddenapi/hiddenapi-unsupported.txt +++ b/boot/hiddenapi/hiddenapi-unsupported.txt @@ -133,8 +133,6 @@ Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)La Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ICountryListener$Stub;-><init>()V -Landroid/location/IGeocodeProvider$Stub;-><init>()V -Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider; Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/location/ILocationListener$Stub;-><init>()V diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING index f1e4d0ee4906..fd571c95f568 100644 --- a/core/TEST_MAPPING +++ b/core/TEST_MAPPING @@ -20,15 +20,5 @@ "core/tests/coretests/src/com/android/internal/inputmethod/.*" ] } - ], - "postsubmit": [ - { - "name": "CtsContactKeysManagerTestCases", - "options": [ - { - "include-filter": "android.provider.cts.contactkeys." - } - ] - } - ] + ] } diff --git a/core/api/current.txt b/core/api/current.txt index 5e8ccdb27f72..4af2c5271d20 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -144,10 +144,12 @@ package android { field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT"; field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL"; field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"; + field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"; field public static final String MANAGE_DEVICE_POLICY_BLUETOOTH = "android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH"; field public static final String MANAGE_DEVICE_POLICY_BUGREPORT = "android.permission.MANAGE_DEVICE_POLICY_BUGREPORT"; field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS"; field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA"; + field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"; field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"; field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"; field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"; @@ -169,6 +171,7 @@ package android { field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"; field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA"; field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"; + field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"; field public static final String MANAGE_DEVICE_POLICY_MOBILE_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK"; field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"; field public static final String MANAGE_DEVICE_POLICY_MTE = "android.permission.MANAGE_DEVICE_POLICY_MTE"; @@ -454,6 +457,7 @@ package android { field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowClickWhenDisabled = 16844312; // 0x1010618 + field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow; field public static final int allowEmbedded = 16843765; // 0x10103f5 field public static final int allowGameAngleDriver = 16844376; // 0x1010658 field public static final int allowGameDownscaling = 16844377; // 0x1010659 @@ -687,6 +691,7 @@ package android { field public static final int defaultHeight = 16844021; // 0x10104f5 field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale; field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504 + field @FlaggedApi("android.nfc.Flags.FLAG_OBSERVE_MODE") public static final int defaultToObserveMode; field public static final int defaultValue = 16843245; // 0x10101ed field public static final int defaultWidth = 16844020; // 0x10104f4 field public static final int delay = 16843212; // 0x10101cc @@ -1491,6 +1496,7 @@ package android { field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261 field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d field public static final int shell = 16844180; // 0x1010594 + field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang; field public static final int shortcutDisabledMessage = 16844075; // 0x101052b field public static final int shortcutId = 16844072; // 0x1010528 field public static final int shortcutLongLabel = 16844074; // 0x101052a @@ -1887,6 +1893,7 @@ package android { field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 field public static final int windowIsFloating = 16842839; // 0x1010057 + field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced; field public static final int windowIsTranslucent = 16842840; // 0x1010058 field public static final int windowLayoutAffinity = 16844313; // 0x1010619 field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586 @@ -4423,12 +4430,14 @@ package android.app { method @Deprecated public void finishFromChild(android.app.Activity); method @Nullable public android.app.ActionBar getActionBar(); method public final android.app.Application getApplication(); + method @FlaggedApi("android.security.content_uri_permission_apis") @Nullable public android.app.ComponentCaller getCaller(); method @Nullable public android.content.ComponentName getCallingActivity(); method @Nullable public String getCallingPackage(); method public int getChangingConfigurations(); method public android.content.ComponentName getComponentName(); method public android.transition.Scene getContentScene(); method public android.transition.TransitionManager getContentTransitionManager(); + method @FlaggedApi("android.security.content_uri_permission_apis") @NonNull public android.app.ComponentCaller getCurrentCaller(); method @Nullable public android.view.View getCurrentFocus(); method @Deprecated public android.app.FragmentManager getFragmentManager(); method @FlaggedApi("android.security.content_uri_permission_apis") @NonNull public android.app.ComponentCaller getInitialCaller(); @@ -4480,6 +4489,7 @@ package android.app { method @CallSuper public void onActionModeStarted(android.view.ActionMode); method public void onActivityReenter(int, android.content.Intent); method protected void onActivityResult(int, int, android.content.Intent); + method @FlaggedApi("android.security.content_uri_permission_apis") public void onActivityResult(int, int, @NonNull android.content.Intent, @NonNull android.app.ComponentCaller); method @Deprecated public void onAttachFragment(android.app.Fragment); method public void onAttachedToWindow(); method @Deprecated public void onBackPressed(); @@ -4521,6 +4531,7 @@ package android.app { method public boolean onNavigateUp(); method @Deprecated public boolean onNavigateUpFromChild(android.app.Activity); method protected void onNewIntent(android.content.Intent); + method @FlaggedApi("android.security.content_uri_permission_apis") public void onNewIntent(@NonNull android.content.Intent, @NonNull android.app.ComponentCaller); method public boolean onOptionsItemSelected(@NonNull android.view.MenuItem); method public void onOptionsMenuClosed(android.view.Menu); method public void onPanelClosed(int, @NonNull android.view.Menu); @@ -4608,6 +4619,7 @@ package android.app { method public void setImmersive(boolean); method public void setInheritShowWhenLocked(boolean); method public void setIntent(android.content.Intent); + method @FlaggedApi("android.security.content_uri_permission_apis") public void setIntent(@Nullable android.content.Intent, @Nullable android.app.ComponentCaller); method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle); method public final void setMediaController(android.media.session.MediaController); method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams); @@ -5329,6 +5341,7 @@ package android.app { method public int getStartType(); method public int getStartupState(); method @NonNull public java.util.Map<java.lang.Integer,java.lang.Long> getStartupTimestamps(); + method @FlaggedApi("android.content.pm.stay_stopped") public boolean wasForceStopped(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationStartInfo> CREATOR; field public static final int LAUNCH_MODE_SINGLE_INSTANCE = 2; // 0x2 @@ -5458,11 +5471,15 @@ package android.app { method public int getDeferralPolicy(); method @Nullable public String getDeliveryGroupMatchingKey(); method public int getDeliveryGroupPolicy(); + method @FlaggedApi("android.app.bcast_event_timestamps") public long getEventTriggerTimestampMillis(); + method @FlaggedApi("android.app.bcast_event_timestamps") public long getRemoteEventTriggerTimestampMillis(); method public boolean isShareIdentityEnabled(); method @NonNull public static android.app.BroadcastOptions makeBasic(); method @NonNull public android.app.BroadcastOptions setDeferralPolicy(int); method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String); method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int); + method @FlaggedApi("android.app.bcast_event_timestamps") public void setEventTriggerTimestampMillis(long); + method @FlaggedApi("android.app.bcast_event_timestamps") public void setRemoteEventTriggerTimestampMillis(long); method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean); method @NonNull public android.os.Bundle toBundle(); field public static final int DEFERRAL_POLICY_DEFAULT = 0; // 0x0 @@ -6090,6 +6107,7 @@ package android.app { method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnDestroy(android.app.Activity); method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent); + method @FlaggedApi("android.security.content_uri_permission_apis") public void callActivityOnNewIntent(@NonNull android.app.Activity, @NonNull android.content.Intent, @NonNull android.app.ComponentCaller); method public void callActivityOnPause(android.app.Activity); method public void callActivityOnPictureInPictureRequested(@NonNull android.app.Activity); method public void callActivityOnPostCreate(@NonNull android.app.Activity, @Nullable android.os.Bundle); @@ -7955,6 +7973,7 @@ package android.app.admin { field public static final String LOCK_TASK_POLICY = "lockTask"; field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended"; field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked"; + field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity"; field public static final String PERMISSION_GRANT_POLICY = "permissionGrant"; field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity"; field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken"; @@ -8015,7 +8034,7 @@ package android.app.admin { method public CharSequence getDeviceOwnerLockScreenInfo(); method @Nullable public String getDevicePolicyManagementRoleHolderPackage(); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); - method @NonNull public String getEnrollmentSpecificId(); + method @FlaggedApi("android.app.admin.flags.permission_migration_for_zero_trust_api_enabled") @NonNull @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES, conditional=true) public String getEnrollmentSpecificId(); method @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET, conditional=true) public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName); method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); @@ -8054,7 +8073,7 @@ package android.app.admin { method @Deprecated public int getPasswordMinimumSymbols(@Nullable android.content.ComponentName); method @Deprecated public int getPasswordMinimumUpperCase(@Nullable android.content.ComponentName); method @Deprecated public int getPasswordQuality(@Nullable android.content.ComponentName); - method @Nullable public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@NonNull android.content.ComponentName); + method @FlaggedApi("android.app.admin.flags.permission_migration_for_zero_trust_api_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional=true) public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@Nullable android.content.ComponentName); method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, conditional=true) public int getPermissionGrantState(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); method public int getPermissionPolicy(android.content.ComponentName); method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName); @@ -8111,6 +8130,7 @@ package android.app.admin { method public boolean isLogoutEnabled(); method public boolean isManagedProfile(@NonNull android.content.ComponentName); method public boolean isMasterVolumeMuted(@NonNull android.content.ComponentName); + method @FlaggedApi("android.app.admin.flags.is_mte_policy_enforced") public static boolean isMtePolicyEnforced(); method public boolean isNetworkLoggingEnabled(@Nullable android.content.ComponentName); method public boolean isOrganizationOwnedDeviceWithManagedProfile(); method public boolean isOverrideApnEnabled(@NonNull android.content.ComponentName); @@ -12383,6 +12403,8 @@ package android.content.pm { method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); + method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isManagedProfile(@NonNull android.os.UserHandle); + method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isProfile(@NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity); method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle); method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); @@ -12473,8 +12495,11 @@ package android.content.pm { public class LauncherApps { method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions(); + method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getAppMarketActivityIntent(@Nullable String, @NonNull android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; + method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle); method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); + method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle); method public java.util.List<android.os.UserHandle> getProfiles(); method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo); @@ -12556,6 +12581,14 @@ package android.content.pm { field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400 } + @FlaggedApi("android.os.allow_private_profile") public final class LauncherUserInfo implements android.os.Parcelable { + method @FlaggedApi("android.os.allow_private_profile") public int describeContents(); + method @FlaggedApi("android.os.allow_private_profile") public int getUserSerialNumber(); + method @FlaggedApi("android.os.allow_private_profile") @NonNull public String getUserType(); + method @FlaggedApi("android.os.allow_private_profile") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("android.os.allow_private_profile") @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherUserInfo> CREATOR; + } + public final class ModuleInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public CharSequence getName(); @@ -15669,6 +15702,7 @@ package android.graphics { method public boolean clipOutRect(@NonNull android.graphics.Rect); method public boolean clipOutRect(float, float, float, float); method public boolean clipOutRect(int, int, int, int); + method @FlaggedApi("com.android.graphics.hwui.flags.clip_shader") public void clipOutShader(@NonNull android.graphics.Shader); method @Deprecated public boolean clipPath(@NonNull android.graphics.Path, @NonNull android.graphics.Region.Op); method public boolean clipPath(@NonNull android.graphics.Path); method @Deprecated public boolean clipRect(@NonNull android.graphics.RectF, @NonNull android.graphics.Region.Op); @@ -15678,6 +15712,7 @@ package android.graphics { method @Deprecated public boolean clipRect(float, float, float, float, @NonNull android.graphics.Region.Op); method public boolean clipRect(float, float, float, float); method public boolean clipRect(int, int, int, int); + method @FlaggedApi("com.android.graphics.hwui.flags.clip_shader") public void clipShader(@NonNull android.graphics.Shader); method public void concat(@Nullable android.graphics.Matrix); method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void concat44(@Nullable android.graphics.Matrix44); method public void disableZ(); @@ -18022,24 +18057,6 @@ package android.graphics.pdf { method public android.graphics.pdf.PdfDocument.PageInfo.Builder setContentRect(android.graphics.Rect); } - public final class PdfRenderer implements java.lang.AutoCloseable { - ctor public PdfRenderer(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException; - method public void close(); - method public int getPageCount(); - method public android.graphics.pdf.PdfRenderer.Page openPage(int); - method public boolean shouldScaleForPrinting(); - } - - public final class PdfRenderer.Page implements java.lang.AutoCloseable { - method public void close(); - method public int getHeight(); - method public int getIndex(); - method public int getWidth(); - method public void render(@NonNull android.graphics.Bitmap, @Nullable android.graphics.Rect, @Nullable android.graphics.Matrix, int); - field public static final int RENDER_MODE_FOR_DISPLAY = 1; // 0x1 - field public static final int RENDER_MODE_FOR_PRINT = 2; // 0x2 - } - } package android.graphics.text { @@ -18563,6 +18580,7 @@ package android.hardware { @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable { method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public int describeContents(); + method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isCombinationSupported(int, int); method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isMixedColorSpacesSupported(); method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("android.hardware.flags.overlayproperties_class_api") @NonNull public static final android.os.Parcelable.Creator<android.hardware.OverlayProperties> CREATOR; @@ -19274,6 +19292,7 @@ package android.hardware.camera2 { method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions(); method public boolean isCaptureProcessProgressAvailable(int); method public boolean isPostviewAvailable(int); + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Float>> EFV_PADDING_ZOOM_FACTOR_RANGE; field public static final int EXTENSION_AUTOMATIC = 0; // 0x0 field @Deprecated public static final int EXTENSION_BEAUTY = 1; // 0x1 field public static final int EXTENSION_BOKEH = 2; // 0x2 @@ -19295,6 +19314,7 @@ package android.hardware.camera2 { public abstract static class CameraExtensionSession.ExtensionCaptureCallback { ctor public CameraExtensionSession.ExtensionCaptureCallback(); method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, int); method public void onCaptureProcessProgressed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @IntRange(from=0, to=100) int); method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest); method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult); @@ -19875,6 +19895,32 @@ package android.hardware.camera2 { field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100 } + @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class ExtensionCaptureRequest { + ctor public ExtensionCaptureRequest(); + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_ROTATE_VIEWPORT; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EFV_STABILIZATION_MODE; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_GIMBAL = 1; // 0x1 + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_LOCKED = 2; // 0x2 + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_OFF = 0; // 0x0 + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.util.Pair<java.lang.Integer,java.lang.Integer>> EFV_TRANSLATE_VIEWPORT; + } + + @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class ExtensionCaptureResult { + ctor public ExtensionCaptureResult(); + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_PADDING_REGION; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_ROTATE_VIEWPORT; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EFV_STABILIZATION_MODE; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES; + field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Integer,java.lang.Integer>> EFV_TRANSLATE_VIEWPORT; + } + public class MultiResolutionImageReader implements java.lang.AutoCloseable { ctor public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int); method public void close(); @@ -19960,11 +20006,14 @@ package android.hardware.camera2.params { public final class ExtensionSessionConfiguration { ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void clearColorSpace(); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") @Nullable public android.graphics.ColorSpace getColorSpace(); method @NonNull public java.util.concurrent.Executor getExecutor(); method public int getExtension(); method @NonNull public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations(); method @Nullable public android.hardware.camera2.params.OutputConfiguration getPostviewOutputConfiguration(); method @NonNull public android.hardware.camera2.CameraExtensionSession.StateCallback getStateCallback(); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setColorSpace(@NonNull android.graphics.ColorSpace.Named); method public void setPostviewOutputConfiguration(@Nullable android.hardware.camera2.params.OutputConfiguration); } @@ -22689,7 +22738,6 @@ package android.media { public final class MediaCodec.QueueRequest { method public void queue(); - method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setBufferInfos(@NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); @@ -22699,6 +22747,7 @@ package android.media { method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int); method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setMultiFrameEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>, @NonNull java.util.ArrayDeque<android.media.MediaCodec.CryptoInfo>); + method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setMultiFrameLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long); method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String); } @@ -22707,12 +22756,16 @@ package android.media { method @NonNull public String getCanonicalName(); method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(String); method @NonNull public String getName(); + method @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public int getSecurityModel(); method public String[] getSupportedTypes(); method public boolean isAlias(); method public boolean isEncoder(); method public boolean isHardwareAccelerated(); method public boolean isSoftwareOnly(); method public boolean isVendor(); + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_MEMORY_SAFE = 1; // 0x1 + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_SANDBOXED = 0; // 0x0 + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; // 0x2 } public static final class MediaCodecInfo.AudioCapabilities { @@ -23541,6 +23594,9 @@ package android.media { field public static final int COLOR_TRANSFER_LINEAR = 1; // 0x1 field public static final int COLOR_TRANSFER_SDR_VIDEO = 3; // 0x3 field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6 + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE = 2; // 0x2 + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_SANDBOXED = 1; // 0x1 + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 4; // 0x4 field public static final String KEY_AAC_DRC_ALBUM_MODE = "aac-drc-album-mode"; field public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level"; field public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level"; @@ -23622,6 +23678,7 @@ package android.media { field public static final String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after"; field public static final String KEY_ROTATION = "rotation-degrees"; field public static final String KEY_SAMPLE_RATE = "sample-rate"; + field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final String KEY_SECURITY_MODEL = "security-model"; field public static final String KEY_SLICE_HEIGHT = "slice-height"; field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers"; field public static final String KEY_STRIDE = "stride"; @@ -26026,7 +26083,6 @@ package android.media.metrics { method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.MediaItemInfo> CREATOR; field public static final long DATA_TYPE_AUDIO = 4L; // 0x4L - field public static final long DATA_TYPE_CUE_POINTS = 128L; // 0x80L field public static final long DATA_TYPE_DEPTH = 16L; // 0x10L field public static final long DATA_TYPE_GAIN_MAP = 32L; // 0x20L field public static final long DATA_TYPE_GAPLESS = 256L; // 0x100L @@ -26035,6 +26091,7 @@ package android.media.metrics { field public static final long DATA_TYPE_IMAGE = 1L; // 0x1L field public static final long DATA_TYPE_METADATA = 8L; // 0x8L field public static final long DATA_TYPE_SPATIAL_AUDIO = 512L; // 0x200L + field public static final long DATA_TYPE_SPEED_SETTING_CUE_POINTS = 128L; // 0x80L field public static final long DATA_TYPE_VIDEO = 2L; // 0x2L field public static final int SOURCE_TYPE_CAMERA = 2; // 0x2 field public static final int SOURCE_TYPE_EDITING_SESSION = 3; // 0x3 @@ -34034,6 +34091,9 @@ package android.os { field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6 field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1 field public static final int USER_OPERATION_SUCCESS = 0; // 0x0 + field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE"; + field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED"; + field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE"; } public static class UserManager.UserOperationException extends java.lang.RuntimeException { @@ -42345,6 +42405,7 @@ package android.telecom { field public static final int SUPPORTS_SET_INACTIVE = 2; // 0x2 field public static final int SUPPORTS_STREAM = 4; // 0x4 field public static final int SUPPORTS_TRANSFER = 8; // 0x8 + field @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public static final int SUPPORTS_VIDEO_CALLING = 16; // 0x10 field public static final int VIDEO_CALL = 2; // 0x2 } @@ -42380,6 +42441,7 @@ package android.telecom { method @NonNull public android.os.ParcelUuid getCallId(); method public void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method @FlaggedApi("com.android.server.telecom.flags.set_mute_state") public void requestMuteState(boolean, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); + method @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public void requestVideoState(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void sendEvent(@NonNull String, @NonNull android.os.Bundle); method public void setActive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void setInactive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); @@ -42428,6 +42490,7 @@ package android.telecom { method public void onCallStreamingFailed(int); method public void onEvent(@NonNull String, @NonNull android.os.Bundle); method public void onMuteStateChanged(boolean); + method @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public default void onVideoStateChanged(int); } public final class CallException extends java.lang.RuntimeException implements android.os.Parcelable { @@ -45731,6 +45794,7 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getSubscriptionsInGroup(@NonNull android.os.ParcelUuid); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isActiveSubscriptionId(int); method public boolean isNetworkRoaming(int); + method @FlaggedApi("com.android.internal.telephony.flags.subscription_user_association_query") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isSubscriptionAssociatedWithUser(int); method public static boolean isUsableSubscriptionId(int); method public static boolean isValidSubscriptionId(int); method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); @@ -47480,6 +47544,7 @@ package android.text { method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float); method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.DynamicLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean); method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean); method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean); @@ -47683,6 +47748,7 @@ package android.text { method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getRightIndents(); method public float getSecondaryHorizontal(int); method public void getSelectionPath(int, int, android.graphics.Path); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang(); method public final float getSpacingAdd(); method public final float getSpacingMultiplier(); method @NonNull public final CharSequence getText(); @@ -47739,6 +47805,7 @@ package android.text { method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int); method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.Layout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setShiftDrawingOffsetForStartOverhang(boolean); method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean); } @@ -48010,6 +48077,7 @@ package android.text { method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float); method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int); method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.StaticLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean); method public android.text.StaticLayout.Builder setText(CharSequence); method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean); @@ -50515,11 +50583,10 @@ package android.view { method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction); method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl); method public default int getBufferTransformHint(); - method @FlaggedApi("com.android.window.flags.get_host_token_api") @Nullable public default android.os.IBinder getHostToken(); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken getInputTransferToken(); method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener); method public default void setChildBoundingInsets(@NonNull android.graphics.Rect); method public default void setTouchableRegion(@Nullable android.graphics.Region); - method @FlaggedApi("com.android.window.flags.transfer_gesture_to_embedded") public default boolean transferHostTouchGestureToEmbedded(@NonNull android.view.SurfaceControlViewHost.SurfacePackage); } @UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener { @@ -52225,6 +52292,7 @@ package android.view { public class SurfaceControlViewHost { ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder); + ctor @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.window.InputTransferToken); method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage(); method @Nullable public android.view.View getView(); method public void relayout(int, int); @@ -52236,6 +52304,7 @@ package android.view { public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable { ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage); method public int describeContents(); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @Nullable public android.window.InputTransferToken getInputTransferToken(); method @NonNull public android.view.SurfaceControl getSurfaceControl(); method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration); method public void notifyDetachedFromWindow(); @@ -52426,7 +52495,7 @@ package android.view { method public final void cancelPendingInputEvents(); method public boolean checkInputConnectionProxy(android.view.View); method public void clearAnimation(); - method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void clearCredentialManagerRequest(); method public void clearFocus(); method public void clearViewTranslationCallback(); method public static int combineMeasuredStates(int, int); @@ -52536,8 +52605,8 @@ package android.view { method @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public final int getContentSensitivity(); method @UiContext public final android.content.Context getContext(); method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo(); - method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback(); - method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.credentials.GetCredentialRequest getCredentialManagerRequest(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public final android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public final android.credentials.GetCredentialRequest getCredentialManagerRequest(); method public final boolean getDefaultFocusHighlightEnabled(); method public static int getDefaultSize(int, int); method public android.view.Display getDisplay(); @@ -52922,7 +52991,7 @@ package android.view { method public void setContentDescription(CharSequence); method @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public final void setContentSensitivity(int); method public void setContextClickable(boolean); - method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public void setDefaultFocusHighlightEnabled(boolean); method @Deprecated public void setDrawingCacheBackgroundColor(@ColorInt int); method @Deprecated public void setDrawingCacheEnabled(boolean); @@ -53196,7 +53265,7 @@ package android.view { field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET; field protected static final int[] PRESSED_STATE_SET; field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET; - field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0.0f; + field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = (0.0f/0.0f); field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4.0f; field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -2.0f; field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -3.0f; @@ -53801,11 +53870,11 @@ package android.view { method public abstract int addChildCount(int); method public abstract void asyncCommit(); method public abstract android.view.ViewStructure asyncNewChild(int); - method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void clearCredentialManagerRequest(); method @Nullable public abstract android.view.autofill.AutofillId getAutofillId(); method public abstract int getChildCount(); - method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback(); - method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.credentials.GetCredentialRequest getCredentialManagerRequest(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback(); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public android.credentials.GetCredentialRequest getCredentialManagerRequest(); method public abstract android.os.Bundle getExtras(); method public abstract CharSequence getHint(); method public abstract CharSequence getText(); @@ -53830,7 +53899,7 @@ package android.view { method public abstract void setClickable(boolean); method public abstract void setContentDescription(CharSequence); method public abstract void setContextClickable(boolean); - method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); + method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public abstract void setDataIsSensitive(boolean); method public abstract void setDimens(int, int, int, int, int, int); method public abstract void setElevation(float); @@ -54015,6 +54084,7 @@ package android.view { method public abstract void invalidatePanelMenu(int); method public final boolean isActive(); method public abstract boolean isFloating(); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean isFrameRatePowerSavingsBalanced(); method public boolean isNavigationBarContrastEnforced(); method public abstract boolean isShortcutKey(int, android.view.KeyEvent); method @Deprecated public boolean isStatusBarContrastEnforced(); @@ -54064,6 +54134,7 @@ package android.view { method public void setFlags(int, int); method public void setFormat(int); method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRatePowerSavingsBalanced(boolean); method public void setGravity(int); method @RequiresPermission(android.Manifest.permission.HIDE_OVERLAY_WINDOWS) public final void setHideOverlayWindows(boolean); method public void setIcon(@DrawableRes int); @@ -54371,15 +54442,17 @@ package android.view { method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); method public default boolean isCrossWindowBlurEnabled(); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerBatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer); method @FlaggedApi("com.android.window.flags.screen_recording_callbacks") @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) public default void removeScreenRecordingCallback(@NonNull java.util.function.Consumer<java.lang.Integer>); method public void removeViewImmediate(android.view.View); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default boolean transferTouchGesture(@NonNull android.window.InputTransferToken, @NonNull android.window.InputTransferToken); method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.view.SurfaceControl); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); + field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1; // 0x1 field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"; field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; field @FlaggedApi("com.android.window.flags.untrusted_embedding_state_sharing") public static final String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING = "android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING"; @@ -54392,6 +54465,7 @@ package android.view { field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"; field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"; field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"; + field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final String PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN = "android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN"; field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE"; field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"; field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"; @@ -54432,6 +54506,7 @@ package android.view { method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled(); method public final CharSequence getTitle(); method public boolean isFitInsetsIgnoringVisibility(); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean isFrameRatePowerSavingsBalanced(); method public boolean isHdrConversionEnabled(); method public static boolean mayUseInputMethod(int); method public void setBlurBehindRadius(@IntRange(from=0) int); @@ -54442,6 +54517,7 @@ package android.view { method public void setFitInsetsSides(int); method public void setFitInsetsTypes(int); method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRatePowerSavingsBalanced(boolean); method public void setHdrConversionEnabled(boolean); method public final void setTitle(CharSequence); method public void setWallpaperTouchEventsEnabled(boolean); @@ -55204,6 +55280,7 @@ package android.view.accessibility { field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6 field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5 field public static final int TYPE_SYSTEM = 3; // 0x3 + field @FlaggedApi("android.view.accessibility.add_type_window_control") public static final int TYPE_WINDOW_CONTROL = 7; // 0x7 } public class CaptioningManager { @@ -60824,6 +60901,7 @@ package android.widget { method public float getShadowDx(); method public float getShadowDy(); method public float getShadowRadius(); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang(); method public final boolean getShowSoftInputOnFocus(); method public CharSequence getText(); method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier(); @@ -60960,6 +61038,7 @@ package android.widget { method public void setSearchResultHighlights(@Nullable int...); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public void setShiftDrawingOffsetForStartOverhang(boolean); method public final void setShowSoftInputOnFocus(boolean); method public void setSingleLine(); method public void setSingleLine(boolean); @@ -61354,6 +61433,12 @@ package android.window { field public static final int EDGE_RIGHT = 1; // 0x1 } + @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public final class InputTransferToken implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.window.InputTransferToken> CREATOR; + } + public interface OnBackAnimationCallback extends android.window.OnBackInvokedCallback { method public default void onBackCancelled(); method public default void onBackProgressed(@NonNull android.window.BackEvent); @@ -61401,11 +61486,11 @@ package android.window { @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public final class TrustedPresentationThresholds implements android.os.Parcelable { ctor @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public int describeContents(); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public float getMinAlpha(); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public float getMinFractionRendered(); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public int getStabilityRequirementMillis(); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @NonNull public static final android.os.Parcelable.Creator<android.window.TrustedPresentationThresholds> CREATOR; - field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minAlpha; - field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minFractionRendered; - field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public final int stabilityRequirementMs; } } diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index b36b963f99c9..1b0da055038d 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -245,6 +245,14 @@ BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SE Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior +CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL: + All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL +CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED: + All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED +CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF: + All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF + + DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match DeprecationMismatch: android.app.Activity#enterPictureInPictureMode(): @@ -1087,6 +1095,14 @@ RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean): Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission +StaticUtils: ExtensionCaptureRequest: + Fully-static utility classes must not have constructor +StaticUtils: android.hardware.camera2.ExtensionCaptureRequest: + Fully-static utility classes must not have constructor +StaticUtils: android.hardware.camera2.ExtensionCaptureResult: + Fully-static utility classes must not have constructor + + Todo: android.hardware.camera2.params.StreamConfigurationMap: Documentation mentions 'TODO' Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor): @@ -1445,6 +1461,15 @@ UnflaggedApi: android.graphics.text.PositionedGlyphs#getItalicOverride(int): New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getItalicOverride(int) UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int): New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int) + +UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest: + New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest +UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest(): + New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest() +UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult: + New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult +UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult(): + New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult() UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR: New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER: diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 531aede0ac9a..af5ea217ff3f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -100,6 +100,7 @@ package android { field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED"; field @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission") public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER"; field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER"; + field @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public static final String CAMERA_PRIVACY_ALLOWLIST = "android.permission.CAMERA_PRIVACY_ALLOWLIST"; field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD"; field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD"; field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT"; @@ -194,6 +195,8 @@ package android { field public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS"; field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS"; + field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"; + field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String MANAGE_DEVICE_POLICY_THEFT_DETECTION = "android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION"; field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES"; field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS"; field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; @@ -592,6 +595,7 @@ package android.app { field public static final int FOREGROUND_SERVICE_API_TYPE_MICROPHONE = 6; // 0x6 field public static final int FOREGROUND_SERVICE_API_TYPE_PHONE_CALL = 7; // 0x7 field public static final int FOREGROUND_SERVICE_API_TYPE_USB = 8; // 0x8 + field @FlaggedApi("android.media.audio.foreground_audio_control") public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 64; // 0x40 field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2 field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1 field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4 @@ -622,6 +626,8 @@ package android.app { method public static String[] getOpStrs(); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[], @NonNull String); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(boolean); method public static int opToDefaultMode(@NonNull String); method @Nullable public static String opToPermission(@NonNull String); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int); @@ -1286,6 +1292,10 @@ package android.app.admin { field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR; } + public final class DevicePolicyIdentifiers { + field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String AUDIT_LOGGING_POLICY = "auditLogging"; + } + public class DevicePolicyKeyguardService extends android.app.Service { ctor public DevicePolicyKeyguardService(); method @Nullable public void dismiss(); @@ -1307,18 +1317,21 @@ package android.app.admin { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.app.admin.DevicePolicyState getDevicePolicyState(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public String getFinancedDeviceKioskRoleHolder(); + method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public java.util.List<android.os.UserHandle> getPolicyManagedProfiles(@NonNull android.os.UserHandle); method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState(); + method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled(); method public boolean isDeviceManaged(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied(); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk(); method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle); + method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION) public boolean isTheftDetectionTriggered(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk(); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean); @@ -1327,8 +1340,11 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException; + method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean); + method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied(); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean); + method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int); method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName); method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle); @@ -3679,6 +3695,7 @@ package android.content { field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA"; + field @FlaggedApi("android.security.frp_enforcement") public static final String ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED = "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION"; field @Deprecated public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; @@ -4672,11 +4689,15 @@ package android.hardware { method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @NonNull @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public java.util.Map<java.lang.String,java.lang.Boolean> getCameraPrivacyAllowlist(); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public int getSensorPrivacyState(int, int); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isCameraPrivacyEnabled(@NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int); method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int); method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyState(int, int); } public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener { @@ -4686,6 +4707,7 @@ package android.hardware { public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams { method public int getSensor(); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public int getState(); method public int getToggleType(); method public boolean isEnabled(); } @@ -4746,9 +4768,13 @@ package android.hardware.camera2.extension { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class CameraOutputSurface { ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public CameraOutputSurface(@NonNull android.view.Surface, @NonNull android.util.Size); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public int getColorSpace(); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public long getDynamicRangeProfile(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int getImageFormat(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.util.Size getSize(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.view.Surface getSurface(); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setColorSpace(int); + method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setDynamicRangeProfile(long); } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public class CharacteristicsMap { @@ -4802,7 +4828,7 @@ package android.hardware.camera2.extension { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface SessionProcessor.CaptureCallback { method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull android.hardware.camera2.CaptureResult); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int, int); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProcessStarted(int); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceCompleted(int); @@ -10229,6 +10255,7 @@ package android.nfc.cardemulation { ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String); method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilterToAutoTransact(@NonNull String); + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean defaultToObserveMode(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream); @@ -10258,6 +10285,7 @@ package android.nfc.cardemulation { 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.nfc_observe_mode") public void setDefaultToObserveMode(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 writeToParcel(@NonNull android.os.Parcel, int); @@ -10432,9 +10460,11 @@ package android.os { method public int getFlags(); method public int getMode(); field public static final int BUGREPORT_FLAG_DEFER_CONSENT = 2; // 0x2 + field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4 field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1 field public static final int BUGREPORT_MODE_FULL = 0; // 0x0 field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1 + field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7 field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2 field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4 field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3 @@ -11084,9 +11114,6 @@ package android.os { field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST"; field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY"; field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM"; - field public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE"; - field public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED"; - field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE"; field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS"; } @@ -11324,6 +11351,7 @@ package android.permission { method public long getLastAccessTimeMillis(); method @NonNull public String getPackageName(); method @NonNull public String getPermissionGroupName(); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull public String getPersistentDeviceId(); method @Nullable public CharSequence getProxyLabel(); method public int getUid(); method public boolean isActive(); @@ -14342,9 +14370,9 @@ package android.telephony { @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public abstract class DomainSelectionService extends android.app.Service { ctor public DomainSelectionService(); + method @NonNull public java.util.concurrent.Executor getCreateExecutor(); method public void onBarringInfoUpdated(int, int, @NonNull android.telephony.BarringInfo); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @NonNull public java.util.concurrent.Executor onCreateExecutor(); method public abstract void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback); method public void onServiceStateUpdated(int, int, @NonNull android.telephony.ServiceState); field public static final int SCAN_TYPE_FULL_SERVICE = 2; // 0x2 @@ -15124,6 +15152,7 @@ package android.telephony { method @Deprecated public boolean getDataEnabled(int); method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion(int); + method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackage(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain(); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index a6505c8b6093..ca9fab815167 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -535,6 +535,10 @@ ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmis Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`) +MethodNameUnits: android.hardware.camera2.extension.CameraOutputSurface#getColorSpace(): + Expected method name units to be `Bytes`, was `Space` in `getColorSpace` + + MissingGetterMatchingBuilder: android.service.voice.HotwordTrainingData.Builder#addTrainingAudio(android.service.voice.HotwordTrainingAudio): android.service.voice.HotwordTrainingData does not declare a `getTrainingAudios()` method matching method android.service.voice.HotwordTrainingData.Builder.addTrainingAudio(android.service.voice.HotwordTrainingAudio) MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index fa232faf07d0..f6366a2afbf0 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1519,6 +1519,7 @@ package android.hardware { public final class SensorPrivacyManager { method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, int, boolean); + method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyState(int, int, int); } public static class SensorPrivacyManager.Sources { @@ -2277,9 +2278,7 @@ package android.os { } public final class BugreportParams { - field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4 field @FlaggedApi("android.os.bugreport_mode_max_value") public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7 - field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7 } public class Build { diff --git a/core/java/android/adaptiveauth/flags.aconfig b/core/java/android/adaptiveauth/flags.aconfig index 39e46bbdfa6a..de4e607b50f1 100644 --- a/core/java/android/adaptiveauth/flags.aconfig +++ b/core/java/android/adaptiveauth/flags.aconfig @@ -1,6 +1,13 @@ package: "android.adaptiveauth" flag { + name: "enable_adaptive_auth" + namespace: "biometrics" + description: "Feature flag for enabling the new adaptive auth service" + bug: "285053096" +} + +flag { name: "report_biometric_auth_attempts" namespace: "biometrics" description: "Control the usage of the biometric auth signal in adaptive auth" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 23fe731701b6..251f82320ae5 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -844,7 +844,32 @@ public class Activity extends ContextThemeWrapper private IBinder mToken; private IBinder mAssistToken; private IBinder mShareableActivityToken; + + /** Initial caller of the activity. Can be retrieved from {@link #getInitialCaller} */ private ComponentCaller mInitialCaller; + /** + * Caller associated with the Intent from {@link #getIntent}. Can be retrieved from + * {@link #getCaller}. + * + * <p>The value of this field depends on how the activity set its intent: + * - If via {@link #setIntent(Intent)}, the caller will be {@code null}. + * - If via {@link #setIntent(Intent, ComponentCaller)}, the caller will be set to the passed + * caller. + */ + private ComponentCaller mCaller; + /** + * Caller associated with an Intent within {@link #onNewIntent} and {@link #onActivityResult}. + * Can be retrieved from either of these methods: + * - {@link #getCurrentCaller} + * - By overriding {@link #onNewIntent(Intent, ComponentCaller)} and getting the second argument + * - By overriding {@link #onActivityResult(int, int, Intent, ComponentCaller)} and getting the + * fourth argument + * + * <p>The value of this field will be {@code null} outside of {@link #onNewIntent} and + * {@link #onActivityResult}. + */ + private ComponentCaller mCurrentCaller; + @UnsupportedAppUsage private int mIdent; @UnsupportedAppUsage @@ -1117,23 +1142,71 @@ public class Activity extends ContextThemeWrapper private static native String getDlWarning(); - /** Return the intent that started this activity. */ + /** + * Returns the intent that started this activity. + * + * <p>To keep the Intent instance for future use, call {@link #setIntent(Intent)}, and use + * this method to retrieve it. + */ public Intent getIntent() { return mIntent; } /** - * Change the intent returned by {@link #getIntent}. This holds a - * reference to the given intent; it does not copy it. Often used in - * conjunction with {@link #onNewIntent}. + * Changes the intent returned by {@link #getIntent}. This holds a + * reference to the given intent; it does not copy it. Often used in + * conjunction with {@link #onNewIntent(Intent)}. * - * @param newIntent The new Intent object to return from getIntent + * @param newIntent The new Intent object to return from {@link #getIntent} * * @see #getIntent - * @see #onNewIntent + * @see #onNewIntent(Intent) */ public void setIntent(Intent newIntent) { + internalSetIntent(newIntent, /* newCaller */ null); + } + + /** + * Returns the ComponentCaller instance of the app that launched this activity with the intent + * from {@link #getIntent()}. To keep the value of the ComponentCaller instance for new intents, + * call {@link #setIntent(Intent, ComponentCaller)} instead of {@link #setIntent(Intent)}. + * + * @return {@link ComponentCaller} instance corresponding to the intent from + * {@link #getIntent()}, or {@code null} if the activity was not launched with that + * intent + * + * @see ComponentCaller + * @see #getIntent + * @see #setIntent(Intent, ComponentCaller) + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public @Nullable ComponentCaller getCaller() { + return mCaller; + } + + /** + * Changes the intent returned by {@link #getIntent}, and ComponentCaller returned by + * {@link #getCaller}. This holds references to the given intent, and ComponentCaller; it does + * not copy them. Often used in conjunction with {@link #onNewIntent(Intent)}. To retrieve the + * caller from {@link #onNewIntent(Intent)}, use {@link #getCurrentCaller}, otherwise override + * {@link #onNewIntent(Intent, ComponentCaller)}. + * + * @param newIntent The new Intent object to return from {@link #getIntent} + * @param newCaller The new {@link ComponentCaller} object to return from + * {@link #getCaller} + * + * @see #getIntent + * @see #onNewIntent(Intent, ComponentCaller) + * @see #getCaller + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public void setIntent(@Nullable Intent newIntent, @Nullable ComponentCaller newCaller) { + internalSetIntent(newIntent, newCaller); + } + + private void internalSetIntent(Intent newIntent, ComponentCaller newCaller) { mIntent = newIntent; + mCaller = newCaller; } /** @@ -2284,18 +2357,42 @@ public class Activity extends ContextThemeWrapper * sometime later when activity becomes active again. * * <p>Note that {@link #getIntent} still returns the original Intent. You - * can use {@link #setIntent} to update it to this new Intent. + * can use {@link #setIntent(Intent)} to update it to this new Intent. * - * @param intent The new intent that was started for the activity. + * @param intent The new intent that was used to start the activity * * @see #getIntent - * @see #setIntent + * @see #setIntent(Intent) * @see #onResume */ protected void onNewIntent(Intent intent) { } /** + * Same as {@link #onNewIntent(Intent)}, but with an extra parameter for the ComponentCaller + * instance associated with the app that sent the intent. + * + * <p>If you want to retrieve the caller without overriding this method, call + * {@link #getCurrentCaller} inside your existing {@link #onNewIntent(Intent)}. + * + * <p>Note that you should only override one {@link #onNewIntent} method. + * + * @param intent The new intent that was used to start the activity + * @param caller The {@link ComponentCaller} instance associated with the app that sent the + * intent + * + * @see ComponentCaller + * @see #onNewIntent(Intent) + * @see #getCurrentCaller + * @see #setIntent(Intent, ComponentCaller) + * @see #getCaller + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public void onNewIntent(@NonNull Intent intent, @NonNull ComponentCaller caller) { + onNewIntent(intent); + } + + /** * The hook for {@link ActivityThread} to save the state of this activity. * * Calls {@link #onSaveInstanceState(android.os.Bundle)} @@ -7044,6 +7141,37 @@ public class Activity extends ContextThemeWrapper } /** + * Returns the ComponentCaller instance of the app that re-launched this activity with a new + * intent via {@link #onNewIntent} or {@link #onActivityResult}. + * + * <p>Note that this method only works within the {@link #onNewIntent} and + * {@link #onActivityResult} methods. If you call this method outside {@link #onNewIntent} and + * {@link #onActivityResult}, it will throw an {@link IllegalStateException}. + * + * <p>You can also retrieve the caller if you override + * {@link #onNewIntent(Intent, ComponentCaller)} or + * {@link #onActivityResult(int, int, Intent, ComponentCaller)}. + * + * <p>To keep the ComponentCaller instance for future use, call + * {@link #setIntent(Intent, ComponentCaller)}, and use {@link #getCaller} to retrieve it. + * + * @return {@link ComponentCaller} instance + * @throws IllegalStateException if the caller is {@code null}, indicating the method was called + * outside {@link #onNewIntent} + * @see ComponentCaller + * @see #setIntent(Intent, ComponentCaller) + * @see #getCaller + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public @NonNull ComponentCaller getCurrentCaller() { + if (mCurrentCaller == null) { + throw new IllegalStateException("The caller is null because #getCurrentCaller should be" + + " called within #onNewIntent method"); + } + return mCurrentCaller; + } + + /** * Control whether this activity's main window is visible. This is intended * only for the special case of an activity that is not going to show a * UI itself, but can't just finish prior to onResume() because it needs @@ -7303,6 +7431,31 @@ public class Activity extends ContextThemeWrapper } /** + * Same as {@link #onActivityResult(int, int, Intent)}, but with an extra parameter for the + * ComponentCaller instance associated with the app that sent the result. + * + * <p>If you want to retrieve the caller without overriding this method, call + * {@link #getCurrentCaller} inside your existing {@link #onActivityResult(int, int, Intent)}. + * + * <p>Note that you should only override one {@link #onActivityResult} method. + * + * @param requestCode The integer request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + * @param caller The {@link ComponentCaller} instance associated with the app that sent the + * intent. + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public void onActivityResult(int requestCode, int resultCode, @NonNull Intent data, + @NonNull ComponentCaller caller) { + onActivityResult(requestCode, resultCode, data); + } + + /** * Called when an activity you launched with an activity transition exposes this * Activity through a returning activity transition, giving you the resultCode * and any additional data from it. This method will only be called if the activity @@ -8740,6 +8893,7 @@ public class Activity extends ContextThemeWrapper if (android.security.Flags.contentUriPermissionApis()) { mInitialCaller = new ComponentCaller(getActivityToken(), initialCallerInfoAccessToken); + mCaller = mInitialCaller; } } @@ -8815,6 +8969,16 @@ public class Activity extends ContextThemeWrapper Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + final void performNewIntent(@NonNull Intent intent, @NonNull ComponentCaller caller) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performNewIntent"); + mCanEnterPictureInPicture = true; + mCurrentCaller = caller; + onNewIntent(intent, caller); + mCurrentCaller = null; + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + } + final void performStart(String reason) { if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performStart:" @@ -9135,15 +9299,36 @@ public class Activity extends ContextThemeWrapper } } + void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data, + ComponentCaller caller, String reason) { + internalDispatchActivityResult(who, requestCode, resultCode, data, caller, reason); + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data, String reason) { + if (android.security.Flags.contentUriPermissionApis()) { + internalDispatchActivityResult(who, requestCode, resultCode, data, + new ComponentCaller(getActivityToken(), /* callerToken */ null), reason); + } else { + internalDispatchActivityResult(who, requestCode, resultCode, data, null, reason); + } + } + + private void internalDispatchActivityResult(String who, int requestCode, int resultCode, + Intent data, ComponentCaller caller, String reason) { if (false) Log.v( TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode + ", resCode=" + resultCode + ", data=" + data); mFragments.noteStateNotSaved(); if (who == null) { - onActivityResult(requestCode, resultCode, data); + if (android.security.Flags.contentUriPermissionApis()) { + mCurrentCaller = caller; + onActivityResult(requestCode, resultCode, data, caller); + mCurrentCaller = null; + } else { + onActivityResult(requestCode, resultCode, data); + } } else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) { who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length()); if (TextUtils.isEmpty(who)) { diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index a59f04bf4f3a..10dc3c6c1360 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -299,6 +299,26 @@ public class ActivityClient { } } + /** Returns the uid of the app that launched the activity. */ + public int getActivityCallerUid(IBinder activityToken, IBinder callerToken) { + try { + return getActivityClientController().getActivityCallerUid(activityToken, + callerToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** Returns the package of the app that launched the activity. */ + public String getActivityCallerPackage(IBinder activityToken, IBinder callerToken) { + try { + return getActivityClientController().getActivityCallerPackage(activityToken, + callerToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** Checks if the app that launched the activity has access to the URI. */ public int checkActivityCallerContentUriPermission(IBinder activityToken, IBinder callerToken, Uri uri, int modeFlags) { diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java index cb06eea2059e..21c67edc607e 100644 --- a/core/java/android/app/ActivityGroup.java +++ b/core/java/android/app/ActivityGroup.java @@ -111,7 +111,7 @@ public class ActivityGroup extends Activity { @Override void dispatchActivityResult(String who, int requestCode, int resultCode, - Intent data, String reason) { + Intent data, ComponentCaller caller, String reason) { if (who != null) { Activity act = mLocalActivityManager.getActivity(who); /* @@ -125,7 +125,7 @@ public class ActivityGroup extends Activity { return; } } - super.dispatchActivityResult(who, requestCode, resultCode, data, reason); + super.dispatchActivityResult(who, requestCode, resultCode, data, caller, reason); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a8d183a1d6dd..237d31c67fe2 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.activityTypeToString; import static android.app.WindowConfiguration.windowingModeToString; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.media.audio.Flags.FLAG_FOREGROUND_AUDIO_CONTROL; import android.Manifest; import android.annotation.ColorInt; @@ -794,6 +795,7 @@ public class ActivityManager { PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK, PROCESS_CAPABILITY_BFSL, PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK, + PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL, }) @Retention(RetentionPolicy.SOURCE) public @interface ProcessCapability {} @@ -943,6 +945,14 @@ public class ActivityManager { public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 1 << 5; /** + * @hide + * Process can access volume APIs and can request audio focus with GAIN. + */ + @FlaggedApi(FLAG_FOREGROUND_AUDIO_CONTROL) + @SystemApi + public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 1 << 6; + + /** * @hide all capabilities, the ORing of all flags in {@link ProcessCapability}. * * Don't expose it as TestApi -- we may add new capabilities any time, which could @@ -953,7 +963,8 @@ public class ActivityManager { | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_BFSL - | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; + | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK + | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; /** * All implicit capabilities. There are capabilities that process automatically have. @@ -975,6 +986,7 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); + pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-'); } /** @hide */ @@ -986,6 +998,7 @@ public class ActivityManager { sb.append((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-'); } /** diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index e14bf68bde53..2a2c5f05f122 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIO import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.view.Display.INVALID_DISPLAY; @@ -1849,7 +1850,7 @@ public class ActivityOptions extends ComponentOptions { public int getPendingIntentLaunchFlags() { // b/243794108: Ignore all flags except the new task flag, to be reconsidered in b/254490217 return mPendingIntentLaunchFlags & - (FLAG_ACTIVITY_NEW_TASK | FLAG_RECEIVER_FOREGROUND); + (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_RECEIVER_FOREGROUND); } /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b25d5ebf61a0..926e297a1098 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3758,7 +3758,7 @@ public final class ActivityThread extends ClientTransactionHandler if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id + " req=" + requestCode + " res=" + resultCode + " data=" + data); final ArrayList<ResultInfo> list = new ArrayList<>(); - list.add(new ResultInfo(id, requestCode, resultCode, data)); + list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken)); final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread); final ActivityResultItem activityResultItem = ActivityResultItem.obtain( activityToken, list); @@ -4203,7 +4203,12 @@ public final class ActivityThread extends ClientTransactionHandler intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo), r.activity.getAttributionSource()); r.activity.mFragments.noteStateNotSaved(); - mInstrumentation.callActivityOnNewIntent(r.activity, intent); + if (android.security.Flags.contentUriPermissionApis()) { + ComponentCaller caller = new ComponentCaller(r.token, intent.mCallerToken); + mInstrumentation.callActivityOnNewIntent(r.activity, intent, caller); + } else { + mInstrumentation.callActivityOnNewIntent(r.activity, intent); + } } } @@ -5794,8 +5799,14 @@ public final class ActivityThread extends ClientTransactionHandler } if (DEBUG_RESULTS) Slog.v(TAG, "Delivering result to activity " + r + " : " + ri); - r.activity.dispatchActivityResult(ri.mResultWho, - ri.mRequestCode, ri.mResultCode, ri.mData, reason); + if (android.security.Flags.contentUriPermissionApis()) { + ComponentCaller caller = new ComponentCaller(r.token, ri.mCallerToken); + r.activity.dispatchActivityResult(ri.mResultWho, + ri.mRequestCode, ri.mResultCode, ri.mData, caller, reason); + } else { + r.activity.dispatchActivityResult(ri.mResultWho, + ri.mRequestCode, ri.mResultCode, ri.mData, reason); + } } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 2100425a6771..16c7753c7f46 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,7 +16,9 @@ package android.app; + import static android.location.flags.Flags.FLAG_LOCATION_BYPASS; +import static android.media.audio.Flags.foregroundAudioControl; import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER; import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED; import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED; @@ -70,6 +72,8 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.permission.PermissionGroupUsage; +import android.permission.PermissionUsageHelper; import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; @@ -224,6 +228,7 @@ public class AppOpsManager { private static Boolean sFullLog = null; final Context mContext; + private PermissionUsageHelper mUsageHelper; @UnsupportedAppUsage final IAppOpsService mService; @@ -3229,6 +3234,10 @@ public class AppOpsManager { * @hide */ public static @Mode int opToDefaultMode(int op) { + if (op == OP_TAKE_AUDIO_FOCUS && foregroundAudioControl()) { + // when removing the flag, change the entry in sAppOpInfos for OP_TAKE_AUDIO_FOCUS + return AppOpsManager.MODE_FOREGROUND; + } return sAppOpInfos[op].defaultMode; } @@ -7759,6 +7768,44 @@ public class AppOpsManager { } /** + * Retrieve current operation state for all applications for a device. + * + * The mode of the ops returned are set for the package but may not reflect their effective + * state due to UID policy or because it's controlled by a different global op. + * + * Use {@link #unsafeCheckOp(String, int, String)}} or + * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed. + * + * @param ops The set of operations you are interested in, or null if you want all of them. + * @param persistentDeviceId The device that the ops are attributed to. + * + * @hide + */ + @SystemApi + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) + public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops, + @NonNull String persistentDeviceId) { + final int[] opCodes; + if (ops != null) { + final int opCount = ops.length; + opCodes = new int[opCount]; + for (int i = 0; i < opCount; i++) { + opCodes[i] = sOpStrToOp.get(ops[i]); + } + } else { + opCodes = null; + } + final List<AppOpsManager.PackageOps> result; + try { + result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return (result != null) ? result : Collections.emptyList(); + } + + /** * Retrieve current operation state for all applications. * * The mode of the ops returned are set for the package but may not reflect their effective @@ -7774,7 +7821,8 @@ public class AppOpsManager { @UnsupportedAppUsage public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) { try { - return mService.getPackagesForOps(ops); + return mService.getPackagesForOpsForDevice(ops, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -9940,6 +9988,30 @@ public class AppOpsManager { appOpsNotedForAttribution.set(op); } + /** + * Get recent op usage data for CAMERA, MICROPHONE and LOCATION from all connected devices + * to power privacy indicator. + * + * @param includeMicrophoneUsage whether to retrieve microphone usage + * @return A list of permission groups currently or recently used by all apps by all users in + * the current profile group. + * + * @hide + */ + @SystemApi + @NonNull + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS) + public List<PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator( + boolean includeMicrophoneUsage) { + // Lazily initialize the usage helper + if (mUsageHelper == null) { + mUsageHelper = new PermissionUsageHelper(mContext); + } + + return mUsageHelper.getOpUsageDataForAllDevices(includeMicrophoneUsage); + } + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index c6712c044539..3715c6e633dc 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.icu.text.SimpleDateFormat; import android.os.Parcel; import android.os.Parcelable; @@ -249,7 +250,7 @@ public final class ApplicationStartInfo implements Parcelable { private @StartType int mStartType; /** - * @see #getStartIntent + * @see #getIntent */ private Intent mStartIntent; @@ -259,6 +260,11 @@ public final class ApplicationStartInfo implements Parcelable { private @LaunchMode int mLaunchMode; /** + * @see #wasForceStopped() + */ + private boolean mWasForceStopped; + + /** * @hide * */ @IntDef( @@ -427,6 +433,15 @@ public final class ApplicationStartInfo implements Parcelable { } /** + * @see #wasForceStopped() + * @param wasForceStopped whether the app had been force-stopped in the past + * @hide + */ + public void setForceStopped(boolean wasForceStopped) { + mWasForceStopped = wasForceStopped; + } + + /** * Current state of startup. * * Can be used to determine whether the object will have additional fields added as it may be @@ -578,6 +593,20 @@ public final class ApplicationStartInfo implements Parcelable { return mLaunchMode; } + /** + * Informs whether this is the first process launch for an app since it was + * {@link ApplicationInfo#FLAG_STOPPED force-stopped} for some reason. + * This allows the app to know if it should re-register for any alarms, jobs and other callbacks + * that were cleared when the app was force-stopped. + * + * @return {@code true} if this is the first process launch of the app after having been + * stopped, {@code false} otherwise. + */ + @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED) + public boolean wasForceStopped() { + return mWasForceStopped; + } + @Override public int describeContents() { return 0; @@ -603,6 +632,7 @@ public final class ApplicationStartInfo implements Parcelable { dest.writeInt(mStartType); dest.writeParcelable(mStartIntent, flags); dest.writeInt(mLaunchMode); + dest.writeBoolean(mWasForceStopped); } /** @hide */ @@ -622,6 +652,7 @@ public final class ApplicationStartInfo implements Parcelable { mStartType = other.mStartType; mStartIntent = other.mStartIntent; mLaunchMode = other.mLaunchMode; + mWasForceStopped = other.mWasForceStopped; } private ApplicationStartInfo(@NonNull Parcel in) { @@ -643,6 +674,7 @@ public final class ApplicationStartInfo implements Parcelable { mStartIntent = in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); mLaunchMode = in.readInt(); + mWasForceStopped = in.readBoolean(); } private static String intern(@Nullable String source) { @@ -720,6 +752,7 @@ public final class ApplicationStartInfo implements Parcelable { intentOut.close(); } proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode); + proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped); proto.end(token); } @@ -799,6 +832,10 @@ public final class ApplicationStartInfo implements Parcelable { case (int) ApplicationStartInfoProto.LAUNCH_MODE: mLaunchMode = proto.readInt(ApplicationStartInfoProto.LAUNCH_MODE); break; + case (int) ApplicationStartInfoProto.WAS_FORCE_STOPPED: + mWasForceStopped = proto.readBoolean( + ApplicationStartInfoProto.WAS_FORCE_STOPPED); + break; } } proto.end(token); @@ -823,6 +860,7 @@ public final class ApplicationStartInfo implements Parcelable { .append(" reason=").append(reasonToString(mReason)) .append(" startType=").append(startTypeToString(mStartType)) .append(" launchMode=").append(mLaunchMode) + .append(" wasForceStopped=").append(mWasForceStopped) .append('\n'); if (mStartIntent != null) { sb.append(" intent=").append(mStartIntent.toString()) @@ -878,7 +916,7 @@ public final class ApplicationStartInfo implements Parcelable { && mDefiningUid == o.mDefiningUid && mReason == o.mReason && mStartupState == o.mStartupState && mStartType == o.mStartType && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName) - && timestampsEquals(o); + && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped; } @Override diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index f727ee5a95fe..1b5b0fc5d917 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -65,6 +67,8 @@ public class BroadcastOptions extends ComponentOptions { private @Nullable BundleMerger mDeliveryGroupExtrasMerger; private @Nullable IntentFilter mDeliveryGroupMatchingFilter; private @DeferralPolicy int mDeferralPolicy; + private @CurrentTimeMillisLong long mEventTriggerTimestampMillis; + private @CurrentTimeMillisLong long mRemoteEventTriggerTimestampMillis; /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { @@ -190,6 +194,18 @@ public class BroadcastOptions extends ComponentOptions { "android:broadcast.idForResponseEvent"; /** + * Corresponds to {@link #setEventTriggerTimestampMillis(long)}. + */ + private static final String KEY_EVENT_TRIGGER_TIMESTAMP = + "android:broadcast.eventTriggerTimestamp"; + + /** + * Corresponds to {@link #setRemoteEventTriggerTimestampMillis(long)}. + */ + private static final String KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP = + "android:broadcast.remoteEventTriggerTimestamp"; + + /** * Corresponds to {@link #setDeliveryGroupPolicy(int)}. */ private static final String KEY_DELIVERY_GROUP_POLICY = @@ -341,6 +357,8 @@ public class BroadcastOptions extends ComponentOptions { mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS); mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID); mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT); + mEventTriggerTimestampMillis = opts.getLong(KEY_EVENT_TRIGGER_TIMESTAMP); + mRemoteEventTriggerTimestampMillis = opts.getLong(KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP); mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY, DELIVERY_GROUP_POLICY_ALL); mDeliveryGroupMatchingNamespaceFragment = opts.getString(KEY_DELIVERY_GROUP_NAMESPACE); @@ -787,6 +805,60 @@ public class BroadcastOptions extends ComponentOptions { } /** + * Set the timestamp for the event that triggered this broadcast, in + * {@link System#currentTimeMillis()} timebase. + * + * <p> For instance, if this broadcast is for a push message, then this timestamp + * could correspond to when the device received the message. + * + * @param timestampMillis the timestamp in {@link System#currentTimeMillis()} timebase that + * correspond to the event that triggered this broadcast. + */ + @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS) + public void setEventTriggerTimestampMillis(@CurrentTimeMillisLong long timestampMillis) { + mEventTriggerTimestampMillis = timestampMillis; + } + + /** + * Return the timestamp for the event that triggered this broadcast, in + * {@link System#currentTimeMillis()} timebase. + * + * @return the timestamp in {@link System#currentTimeMillis()} timebase that was previously + * set using {@link #setEventTriggerTimestampMillis(long)}. + */ + @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS) + public @CurrentTimeMillisLong long getEventTriggerTimestampMillis() { + return mEventTriggerTimestampMillis; + } + + /** + * Set the timestamp for the remote event, if any, that triggered this broadcast, in + * {@link System#currentTimeMillis()} timebase. + * + * <p> For instance, if this broadcast is for a push message, then this timestamp + * could correspond to when the message originated remotely. + * + * @param timestampMillis the timestamp in {@link System#currentTimeMillis()} timebase that + * correspond to the remote event that triggered this broadcast. + */ + @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS) + public void setRemoteEventTriggerTimestampMillis(@CurrentTimeMillisLong long timestampMillis) { + mRemoteEventTriggerTimestampMillis = timestampMillis; + } + + /** + * Return the timestamp for the remote event that triggered this broadcast, in + * {@link System#currentTimeMillis()} timebase. + * + * @return the timestamp in {@link System#currentTimeMillis()} timebase that was previously + * set using {@link #setRemoteEventTriggerTimestampMillis(long)}}. + */ + @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS) + public @CurrentTimeMillisLong long getRemoteEventTriggerTimestampMillis() { + return mRemoteEventTriggerTimestampMillis; + } + + /** * Sets deferral policy for this broadcast that specifies how this broadcast * can be deferred for delivery at some future point. */ @@ -1120,6 +1192,12 @@ public class BroadcastOptions extends ComponentOptions { if (mIdForResponseEvent != 0) { b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent); } + if (mEventTriggerTimestampMillis > 0) { + b.putLong(KEY_EVENT_TRIGGER_TIMESTAMP, mEventTriggerTimestampMillis); + } + if (mRemoteEventTriggerTimestampMillis > 0) { + b.putLong(KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP, mRemoteEventTriggerTimestampMillis); + } if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) { b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy); } diff --git a/core/java/android/app/ComponentCaller.java b/core/java/android/app/ComponentCaller.java index 44e8a0a3a20c..14bc0038883a 100644 --- a/core/java/android/app/ComponentCaller.java +++ b/core/java/android/app/ComponentCaller.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; import android.content.pm.PackageManager; @@ -24,8 +25,6 @@ import android.net.Uri; import android.os.IBinder; import android.os.Process; -import androidx.annotation.NonNull; - import java.util.Objects; /** @@ -45,7 +44,7 @@ public final class ComponentCaller { /** * @hide */ - public ComponentCaller(@NonNull IBinder activityToken, @Nullable IBinder callerToken) { + public ComponentCaller(@Nullable IBinder activityToken, @Nullable IBinder callerToken) { mActivityToken = activityToken; mCallerToken = callerToken; } @@ -83,7 +82,7 @@ public final class ComponentCaller { * @see Activity#getLaunchedFromUid() */ public int getUid() { - return ActivityClient.getInstance().getLaunchedFromUid(mActivityToken); + return ActivityClient.getInstance().getActivityCallerUid(mActivityToken, mCallerToken); } /** @@ -121,7 +120,7 @@ public final class ComponentCaller { */ @Nullable public String getPackage() { - return ActivityClient.getInstance().getLaunchedFromPackage(mActivityToken); + return ActivityClient.getInstance().getActivityCallerPackage(mActivityToken, mCallerToken); } /** diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 05fee72b7e61..9c8fea15c712 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -90,7 +90,9 @@ interface IActivityClientController { ComponentName getCallingActivity(in IBinder token); String getCallingPackage(in IBinder token); int getLaunchedFromUid(in IBinder token); + int getActivityCallerUid(in IBinder activityToken, in IBinder callerToken); String getLaunchedFromPackage(in IBinder token); + String getActivityCallerPackage(in IBinder activityToken, in IBinder callerToken); int checkActivityCallerContentUriPermission(in IBinder activityToken, in IBinder callerToken, in Uri uri, int modeFlags, int userId); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 454d60534476..be7199b9a0fc 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1624,26 +1625,75 @@ public class Instrumentation { * @param intent The new intent being received. */ public void callActivityOnNewIntent(Activity activity, Intent intent) { - activity.performNewIntent(intent); + if (android.security.Flags.contentUriPermissionApis()) { + activity.performNewIntent(intent, new ComponentCaller(activity.getActivityToken(), + /* callerToken */ null)); + } else { + activity.performNewIntent(intent); + } + } + + /** + * Same as {@link #callActivityOnNewIntent(Activity, Intent)}, but with an extra parameter for + * the {@link ComponentCaller} instance associated with the app that sent the intent. + * + * @param activity The activity receiving a new Intent. + * @param intent The new intent being received. + * @param caller The {@link ComponentCaller} instance that launched the activity with the new + * intent. + */ + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public void callActivityOnNewIntent(@NonNull Activity activity, @NonNull Intent intent, + @NonNull ComponentCaller caller) { + activity.performNewIntent(intent, caller); } /** * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) { + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent, + @NonNull ComponentCaller caller) { + internalCallActivityOnNewIntent(activity, intent, caller); + } + + @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS) + private void internalCallActivityOnNewIntent(Activity activity, ReferrerIntent intent, + @NonNull ComponentCaller caller) { final String oldReferrer = activity.mReferrer; try { if (intent != null) { activity.mReferrer = intent.mReferrer; } - callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null); + Intent newIntent = intent != null ? new Intent(intent) : null; + callActivityOnNewIntent(activity, newIntent, caller); } finally { activity.mReferrer = oldReferrer; } } /** + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) { + if (android.security.Flags.contentUriPermissionApis()) { + internalCallActivityOnNewIntent(activity, intent, new ComponentCaller( + activity.getActivityToken(), /* callerToken */ null)); + } else { + final String oldReferrer = activity.mReferrer; + try { + if (intent != null) { + activity.mReferrer = intent.mReferrer; + } + callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null); + } finally { + activity.mReferrer = oldReferrer; + } + } + } + + /** * Perform calling of an activity's {@link Activity#onStart} * method. The default implementation simply calls through to that method. * diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index f92ff83919c7..da0cc01e363d 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -31,6 +31,7 @@ per-file Service* = file:/services/core/java/com/android/server/am/OWNERS per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS per-file *UiAutomation* = file:/services/accessibility/OWNERS +per-file *UiAutomation* = file:/core/java/android/permission/OWNERS per-file GameManager* = file:/GAME_MANAGER_OWNERS per-file GameMode* = file:/GAME_MANAGER_OWNERS per-file GameState* = file:/GAME_MANAGER_OWNERS diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java index 535f69f0e264..213c38c71543 100644 --- a/core/java/android/app/ResultInfo.java +++ b/core/java/android/app/ResultInfo.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Build; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -36,14 +37,21 @@ public class ResultInfo implements Parcelable { public final int mResultCode; @UnsupportedAppUsage public final Intent mData; + public final IBinder mCallerToken; @UnsupportedAppUsage public ResultInfo(String resultWho, int requestCode, int resultCode, Intent data) { + this(resultWho, requestCode, resultCode, data, /* callerToken */ null); + } + + public ResultInfo(String resultWho, int requestCode, int resultCode, + Intent data, IBinder callerToken) { mResultWho = resultWho; mRequestCode = requestCode; mResultCode = resultCode; mData = data; + mCallerToken = callerToken; } public String toString() { @@ -65,6 +73,7 @@ public class ResultInfo implements Parcelable { } else { out.writeInt(0); } + out.writeStrongBinder(mCallerToken); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -88,6 +97,7 @@ public class ResultInfo implements Parcelable { } else { mData = null; } + mCallerToken = in.readStrongBinder(); } @Override @@ -100,7 +110,8 @@ public class ResultInfo implements Parcelable { : mData.filterEquals(other.mData); return intentsEqual && Objects.equals(mResultWho, other.mResultWho) && mResultCode == other.mResultCode - && mRequestCode == other.mRequestCode; + && mRequestCode == other.mRequestCode + && mCallerToken == other.mCallerToken; } @Override @@ -112,6 +123,7 @@ public class ResultInfo implements Parcelable { if (mData != null) { result = 31 * result + mData.filterHashCode(); } + result = 31 * result + Objects.hashCode(mCallerToken); return result; } } diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig index ff23f09f78a5..350b1edf2129 100644 --- a/core/java/android/app/activity_manager.aconfig +++ b/core/java/android/app/activity_manager.aconfig @@ -34,3 +34,10 @@ flag { description: "Add a new callback in Service to indicate a FGS has reached its timeout." bug: "317799821" } + +flag { + name: "bcast_event_timestamps" + namespace: "backstage_power" + description: "Add APIs for clients to provide broadcast event trigger timestamps" + bug: "325136414" +} diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java index 318b2fb7b980..3c56aaf33ef3 100644 --- a/core/java/android/app/admin/DevicePolicyIdentifiers.java +++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java @@ -20,6 +20,7 @@ import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED; import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.admin.flags.Flags; import android.os.UserManager; @@ -53,6 +54,15 @@ public final class DevicePolicyIdentifiers { public static final String SECURITY_LOGGING_POLICY = "securityLogging"; /** + * String identifier for {@link DevicePolicyManager#setAuditLogEnabled}. + * + * @hide + */ + @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED) + @SystemApi + public static final String AUDIT_LOGGING_POLICY = "auditLogging"; + + /** * String identifier for {@link DevicePolicyManager#setLockTaskPackages}. */ public static final String LOCK_TASK_POLICY = "lockTask"; @@ -182,6 +192,12 @@ public final class DevicePolicyIdentifiers { public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling"; /** + * String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}. + */ + @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED) + public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity"; + + /** * @hide */ public static final String USER_RESTRICTION_PREFIX = "userRestriction_"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5c6308f209fe..a6fda9d23aca 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -37,6 +37,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE; @@ -44,6 +45,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA; @@ -52,7 +54,10 @@ import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY; import static android.Manifest.permission.SET_TIME; import static android.Manifest.permission.SET_TIME_ZONE; import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED; +import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED; +import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED; import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; +import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; @@ -151,6 +156,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.org.conscrypt.TrustedCertificateStore; +import com.android.internal.os.Zygote; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; @@ -228,7 +234,6 @@ public class DevicePolicyManager { private final boolean mParentInstance; private final DevicePolicyResourcesManager mResourcesManager; - /** @hide */ public DevicePolicyManager(Context context, IDevicePolicyManager service) { this(context, service, false); @@ -4115,6 +4120,19 @@ public class DevicePolicyManager { return MTE_NOT_CONTROLLED_BY_POLICY; } + /** + * Get the current MTE state of the device. + * + * <a href="https://source.android.com/docs/security/test/memory-safety/arm-mte"> + * Learn more about MTE</a> + * + * @return whether MTE is currently enabled on the device. + */ + @FlaggedApi(FLAG_IS_MTE_POLICY_ENFORCED) + public static boolean isMtePolicyEnforced() { + return Zygote.nativeSupportsMemoryTagging(); + } + /** Indicates that content protection is not controlled by policy, allowing user to choose. */ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED) public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; @@ -13415,17 +13433,25 @@ public class DevicePolicyManager { } /** - * Called by device or profile owners to get information about a pending system update. + * Get information about a pending system update. + * + * Can be called by device or profile owners, and starting from Android + * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, holders of the permission + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}. * * @param admin Which profile or device owner this request is associated with. * @return Information about a pending system update or {@code null} if no update pending. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a device, profile owner or holders of + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}. * @see DeviceAdminReceiver#onSystemUpdatePending(Context, Intent, long) */ - public @Nullable SystemUpdateInfo getPendingSystemUpdate(@NonNull ComponentName admin) { + @RequiresPermission(value = MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional = true) + @SuppressLint("RequiresPermission") + @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED) + public @Nullable SystemUpdateInfo getPendingSystemUpdate(@Nullable ComponentName admin) { throwIfParentInstance("getPendingSystemUpdate"); try { - return mService.getPendingSystemUpdate(admin); + return mService.getPendingSystemUpdate(admin, mContext.getPackageName()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -14033,6 +14059,74 @@ public class DevicePolicyManager { } /** + * Controls whether audit logging is enabled. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED) + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void setAuditLogEnabled(boolean enabled) { + throwIfParentInstance("setAuditLogEnabled"); + try { + mService.setAuditLogEnabled(mContext.getPackageName(), true); + } catch (RemoteException re) { + re.rethrowFromSystemServer(); + } + } + + /** + * @return Whether audit logging is enabled. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED) + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public boolean isAuditLogEnabled() { + throwIfParentInstance("isAuditLogEnabled"); + try { + return mService.isAuditLogEnabled(mContext.getPackageName()); + } catch (RemoteException re) { + re.rethrowFromSystemServer(); + // unreachable + return false; + } + } + + /** + * Sets audit log event callback. Only one callback per UID is active at any time, when a new + * callback is set, the previous one is forgotten. Should only be called when audit log policy + * is enforced by the caller. Disabling the policy clears the callback. Each time a new callback + * is set, it will first be invoked with all the audit log events available at the time. + * + * @param callback callback to invoke when new audit log events become available or {@code null} + * to clear the callback. + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED) + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void setAuditLogEventCallback( + @NonNull @CallbackExecutor Executor executor, + @Nullable Consumer<List<SecurityEvent>> callback) { + throwIfParentInstance("setAuditLogEventCallback"); + final IAuditLogEventsCallback wrappedCallback = callback == null + ? null + : new IAuditLogEventsCallback.Stub() { + @Override + public void onNewAuditLogEvents(List<SecurityEvent> events) { + executor.execute(() -> callback.accept(events)); + } + }; + try { + mService.setAuditLogEventsCallback(mContext.getPackageName(), wrappedCallback); + } catch (RemoteException re) { + re.rethrowFromSystemServer(); + } + } + + /** * Called by device owner or profile owner of an organization-owned managed profile to retrieve * all new security logging entries since the last call to this API after device boots. * @@ -16494,8 +16588,9 @@ public class DevicePolicyManager { * The identifier would be consistent even if the work profile is removed and enrolled again * (to the same organization), or the device is factory reset and re-enrolled. * - * Can only be called by the Profile Owner or Device Owner, if the - * {@link #setOrganizationId(String)} was previously called. + * Can only be called by the Profile Owner and Device Owner, and starting from Android + * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, holders of the permission + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES}. * If {@link #setOrganizationId(String)} was not called, then the returned value will be an * empty string. * @@ -16508,8 +16603,12 @@ public class DevicePolicyManager { * and must switch to using this method. * * @return A stable, enrollment-specific identifier. - * @throws SecurityException if the caller is not a profile owner or device owner. + * @throws SecurityException if the caller is not a profile owner, device owner or holding the + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES} permission */ + @RequiresPermission(value = MANAGE_DEVICE_POLICY_CERTIFICATES, conditional = true) + @SuppressLint("RequiresPermission") + @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED) @NonNull public String getEnrollmentSpecificId() { throwIfParentInstance("getEnrollmentSpecificId"); if (mService == null) { @@ -17029,6 +17128,26 @@ public class DevicePolicyManager { } /** + * + * Returns whether the device considers itself to be potentially stolen. + * @hide + */ + @SystemApi + @RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION) + @FlaggedApi(Flags.FLAG_DEVICE_THEFT_API_ENABLED) + public boolean isTheftDetectionTriggered() { + throwIfParentInstance("isTheftDetectionTriggered"); + if (mService == null) { + return false; + } + try { + return mService.isTheftDetectionTriggered(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns a {@link DevicePolicyResourcesManager} containing the required APIs to set, reset, * and get device policy related resources. */ @@ -17348,4 +17467,46 @@ public class DevicePolicyManager { } return new HashSet<>(); } + + /** + * Controls the maximum storage size allowed for policies associated with an admin. + * Setting a limit of -1 effectively removes any storage restrictions. + * + * @param storageLimit Maximum storage allowed in bytes. Use -1 to disable limits. + * + * @hide + */ + @SystemApi + @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED) + public void setMaxPolicyStorageLimit(int storageLimit) { + if (mService != null) { + try { + mService.setMaxPolicyStorageLimit(mContext.getPackageName(), storageLimit); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Retrieves the current maximum storage limit for policies associated with an admin. + * + * @return The maximum storage limit in bytes, or -1 if no limit is enforced. + * + * @hide + */ + @SystemApi + @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED) + public int getMaxPolicyStorageLimit() { + if (mService != null) { + try { + return mService.getMaxPolicyStorageLimit(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return -1; + } }
\ No newline at end of file diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 07ee8de587f8..1aee9fe57466 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -338,4 +338,9 @@ public abstract class DevicePolicyManagerInternal { * Enforces resolved security logging policy, should only be invoked from device policy engine. */ public abstract void enforceSecurityLoggingPolicy(boolean enabled); + + /** + * Enforces resolved audit logging policy, should only be invoked from device policy engine. + */ + public abstract void enforceAuditLoggingPolicy(boolean enabled); } diff --git a/core/java/android/app/admin/IAuditLogEventsCallback.aidl b/core/java/android/app/admin/IAuditLogEventsCallback.aidl new file mode 100644 index 000000000000..ab871178a9b9 --- /dev/null +++ b/core/java/android/app/admin/IAuditLogEventsCallback.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +import android.app.admin.SecurityLog; + +/** @hide */ +oneway interface IAuditLogEventsCallback { + void onNewAuditLogEvents(in List<SecurityLog.SecurityEvent> events); +}
\ No newline at end of file diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f72fdc069db5..3a7a891c7995 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -32,6 +32,7 @@ import android.app.admin.SystemUpdatePolicy; import android.app.admin.PackagePolicy; import android.app.admin.PasswordMetrics; import android.app.admin.FactoryResetProtectionPolicy; +import android.app.admin.IAuditLogEventsCallback; import android.app.admin.ManagedProfileProvisioningParams; import android.app.admin.FullyManagedDeviceProvisioningParams; import android.app.admin.ManagedSubscriptionsPolicy; @@ -392,7 +393,7 @@ interface IDevicePolicyManager { boolean getDoNotAskCredentialsOnBoot(); void notifyPendingSystemUpdate(in SystemUpdateInfo info); - SystemUpdateInfo getPendingSystemUpdate(in ComponentName admin); + SystemUpdateInfo getPendingSystemUpdate(in ComponentName admin, in String callerPackage); void setPermissionPolicy(in ComponentName admin, in String callerPackage, int policy); int getPermissionPolicy(in ComponentName admin); @@ -441,6 +442,10 @@ interface IDevicePolicyManager { long forceNetworkLogs(); long forceSecurityLogs(); + void setAuditLogEnabled(String callerPackage, boolean enabled); + boolean isAuditLogEnabled(String callerPackage); + void setAuditLogEventsCallback(String callerPackage, in IAuditLogEventsCallback callback); + boolean isUninstallInQueue(String packageName); void uninstallPackageWithActiveAdmins(String packageName); @@ -576,6 +581,8 @@ interface IDevicePolicyManager { void setWifiSsidPolicy(String callerPackageName, in WifiSsidPolicy policy); WifiSsidPolicy getWifiSsidPolicy(String callerPackageName); + boolean isTheftDetectionTriggered(String callerPackageName); + List<UserHandle> listForegroundAffiliatedUsers(); void setDrawables(in List<DevicePolicyDrawableResource> drawables); void resetDrawables(in List<String> drawableIds); @@ -615,4 +622,7 @@ interface IDevicePolicyManager { int getContentProtectionPolicy(in ComponentName who, String callerPackageName); int[] getSubscriptionIds(String callerPackageName); + + void setMaxPolicyStorageLimit(String packageName, int storageLimit); + int getMaxPolicyStorageLimit(String packageName); } diff --git a/core/java/android/app/admin/SecurityLog.aidl b/core/java/android/app/admin/SecurityLog.aidl new file mode 100644 index 000000000000..e5ae2df8c21b --- /dev/null +++ b/core/java/android/app/admin/SecurityLog.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +/** @hide */ +parcelable SecurityLog.SecurityEvent;
\ No newline at end of file diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 30cd1b72fd49..cbd8e5b2ec3e 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -92,6 +92,13 @@ flag { } flag { + name: "allow_querying_profile_type" + namespace: "enterprise" + description: "Public APIs to query if a user is a profile and what kind of profile type it is." + bug: "323001115" +} + +flag { name: "quiet_mode_credential_bug_fix" namespace: "enterprise" description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped." @@ -132,3 +139,10 @@ flag { description: "Add Headless DO support." bug: "289515470" } + +flag { + name: "is_mte_policy_enforced" + namespace: "enterprise" + description: "Allow to query whether MTE is enabled or not to check for compliance for enterprise policy" + bug: "322777918" +} diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 7a4a3f9c8f27..9fa73627de05 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1,5 +1,7 @@ package android.app.assist; +import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION; + import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1278,7 +1280,7 @@ public class AssistStructure implements Parcelable { * * @hide */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) @Nullable public GetCredentialRequest getCredentialManagerRequest() { return mGetCredentialRequest; @@ -1291,7 +1293,7 @@ public class AssistStructure implements Parcelable { * @hide * */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) @Nullable public OutcomeReceiver<GetCredentialResponse, GetCredentialException> getCredentialManagerCallback() { diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS new file mode 100644 index 000000000000..6932ba23a8ac --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/OWNERS @@ -0,0 +1,7 @@ +# Bug component: 1363385 + +sandeepbandaru@google.com +shivanker@google.com +hackz@google.com +volnov@google.com + diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e8031a374310..0bcbb8e1868c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -19,6 +19,7 @@ package android.content; import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY; import static android.content.ContentProvider.maybeAddUserId; import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; +import static android.security.Flags.FLAG_FRP_ENFORCEMENT; import static android.service.chooser.Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA; import android.Manifest; @@ -3902,6 +3903,26 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.ACTION_IDLE_MAINTENANCE_END"; /** + * Broadcast Action: A broadcast sent to the main user when the main user changes their + * Lock Screen Knowledge Factor, either because they changed the current value, or because + * they added or removed it. + * + * <p class="note">At present, this intent is only broadcast to listeners with the + * CONFIGURE_FACTORY_RESET_PROTECTION signature|privileged permiession.</p> + * + * <p class="note">This is a protected intent that can only be sent by the system.</p> + * + * @hide + */ + @FlaggedApi(FLAG_FRP_ENFORCEMENT) + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(protectedBroadcast = true) + public static final String + ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED = + "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED"; + + /** * Broadcast Action: a remote intent is to be broadcasted. * * A remote intent is used for remote RPC between devices. The remote intent diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index a8dba5182e90..cae4fab0099c 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1565,6 +1565,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { private Boolean requestRawExternalStorageAccess; /** + * If {@code false}, this app does not allow its activities to be replaced by another app. + * Is set from application manifest application tag's allowCrossUidActivitySwitchFromBelow + * attribute. + * @hide + */ + public boolean allowCrossUidActivitySwitchFromBelow = true; + + /** * Represents the default policy. The actual policy used will depend on other properties of * the application, e.g. the target SDK version. * @hide @@ -1760,6 +1768,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { + Integer.toHexString(localeConfigRes)); } pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled()); + pw.println(prefix + "allowCrossUidActivitySwitchFromBelow=" + + allowCrossUidActivitySwitchFromBelow); + } pw.println(prefix + "createTimestamp=" + createTimestamp); if (mKnownActivityEmbeddingCerts != null) { @@ -1877,6 +1888,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT, nativeHeapZeroInitialized); } + proto.write(ApplicationInfoProto.Detail.ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW, + allowCrossUidActivitySwitchFromBelow); proto.end(detailToken); } if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) { @@ -2002,6 +2015,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized; requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess; localeConfigRes = orig.localeConfigRes; + allowCrossUidActivitySwitchFromBelow = orig.allowCrossUidActivitySwitchFromBelow; createTimestamp = SystemClock.uptimeMillis(); } @@ -2106,6 +2120,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } dest.writeInt(localeConfigRes); + dest.writeInt(allowCrossUidActivitySwitchFromBelow ? 1 : 0); + sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags); } @@ -2204,6 +2220,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } localeConfigRes = source.readInt(); + allowCrossUidActivitySwitchFromBelow = source.readInt() != 0; + mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source); if (mKnownActivityEmbeddingCerts.isEmpty()) { mKnownActivityEmbeddingCerts = null; diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 529363f828bb..8220313a9197 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -19,7 +19,9 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL; import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; +import static android.app.admin.flags.Flags.FLAG_ALLOW_QUERYING_PROFILE_TYPE; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -314,6 +316,41 @@ public class CrossProfileApps { } } + + /** + * Checks if the specified user is a profile, i.e. not the parent user. + * + * @param userHandle The UserHandle of the target profile, must be one of the users returned by + * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will + * be thrown. + * @return whether the specified user is a profile. + */ + @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE) + public boolean isProfile(@NonNull UserHandle userHandle) { + // Note that this is not a security check, but rather a check for correct use. + // The actual security check is performed by UserManager. + verifyCanAccessUser(userHandle); + + return mUserManager.isProfile(userHandle.getIdentifier()); + } + + /** + * Checks if the specified user is a managed profile. + * + * @param userHandle The UserHandle of the target profile, must be one of the users returned by + * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will + * be thrown. + * @return whether the specified user is a managed profile. + */ + @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE) + public boolean isManagedProfile(@NonNull UserHandle userHandle) { + // Note that this is not a security check, but rather a check for correct use. + // The actual security check is performed by UserManager. + verifyCanAccessUser(userHandle); + + return mUserManager.isManagedProfile(userHandle.getIdentifier()); + } + /** * Return a label that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return @@ -677,6 +714,11 @@ public class CrossProfileApps { } } + /** + * A validation method to check that the methods in this class are only being applied to user + * handles returned by {@link #getTargetUserProfiles()}. As this is run client-side for + * input validation purposes, this should never replace a real security check service-side. + */ private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 7c264f65d471..9c859c442355 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -17,6 +17,8 @@ package android.content.pm; import static android.Manifest.permission; +import static android.Manifest.permission.ACCESS_HIDDEN_PROFILES; +import static android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL; import static android.Manifest.permission.READ_FRAME_BUFFER; import android.annotation.CallbackExecutor; @@ -779,15 +781,20 @@ public class LauncherApps { /** * Returns information related to a user which is useful for displaying UI elements - * to distinguish it from other users (eg, badges). Only system launchers should - * call this API. + * to distinguish it from other users (eg, badges). * - * @param userHandle user handle of the user for which LauncherUserInfo is requested - * @return the LauncherUserInfo object related to the user specified. - * @hide + * <p>If the user in question is a hidden profile like + * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have + * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required. + * + * @param userHandle user handle of the user for which LauncherUserInfo is requested. + * @return the {@link LauncherUserInfo} object related to the user specified, null in case + * the user is inaccessible. */ @Nullable @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) + @RequiresPermission(conditional = true, + anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) public final LauncherUserInfo getLauncherUserInfo(@NonNull UserHandle userHandle) { if (DEBUG) { Log.i(TAG, "getLauncherUserInfo " + userHandle); @@ -823,17 +830,20 @@ public class LauncherApps { * </ul> * </p> * - * + * <p>If the user in question is a hidden profile + * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have + * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required. * * @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) + @RequiresPermission(conditional = true, + anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) public IntentSender getAppMarketActivityIntent(@Nullable String packageName, @NonNull UserHandle user) { if (DEBUG) { @@ -851,15 +861,21 @@ public class LauncherApps { /** * 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. + * <p>An empty list denotes that all system packages should be treated as pre-installed for that + * user at creation. + * + * <p>If the user in question is a hidden profile like + * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have + * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required. * * @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) + @NonNull + @RequiresPermission(conditional = true, + anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) public List<String> getPreInstalledSystemPackages(@NonNull UserHandle userHandle) { if (DEBUG) { Log.i(TAG, "getPreInstalledSystemPackages for user: " + userHandle); diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java index 214c3e48db71..8426f54d4754 100644 --- a/core/java/android/content/pm/LauncherUserInfo.java +++ b/core/java/android/content/pm/LauncherUserInfo.java @@ -27,8 +27,6 @@ import android.os.UserManager; /** * The LauncherUserInfo object holds information about an Android user that is required to display * the Launcher related UI elements specific to the user (like badges). - * - * @hide */ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) public final class LauncherUserInfo implements Parcelable { @@ -41,11 +39,9 @@ public final class LauncherUserInfo implements Parcelable { /** * Returns type of the user as defined in {@link UserManager}. e.g., * {@link UserManager.USER_TYPE_PROFILE_MANAGED} or {@link UserManager.USER_TYPE_PROFILE_ClONE} - * TODO(b/303812736): Make the return type public and update javadoc here once the linked bug - * is resolved. + * or {@link UserManager.USER_TYPE_PROFILE_PRIVATE} * * @return the userType for the user whose LauncherUserInfo this is - * @hide */ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @NonNull @@ -58,7 +54,6 @@ public final class LauncherUserInfo implements Parcelable { * {@link UserManager#getSerialNumberForUser(UserHandle)} * * @return the serial number associated with the user - * @hide */ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) public int getUserSerialNumber() { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 49c8a7cad57a..2a673530578a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2554,6 +2554,15 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130; /** + * Installation failed return code: if the system failed to install the package that + * {@link android.R.attr#multiArch} is true in its manifest because its packaged + * native code did not match all of the natively ABIs supported by the system. + * + * @hide + */ + public static final int INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS = -131; + + /** * App minimum aspect ratio set by the user which will override app-defined aspect ratio. * * @hide @@ -10452,6 +10461,8 @@ public abstract class PackageManager { case INSTALL_FAILED_SESSION_INVALID: return "INSTALL_FAILED_SESSION_INVALID"; case INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST: return "INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST"; + case INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS: + return "INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS"; default: return Integer.toString(status); } } diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index c7d93bfbb83d..6696ba035f30 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -139,6 +139,20 @@ flag { flag { name: "enable_launcher_apps_hidden_profile_checks" namespace: "profile_experiences" - description: "Enable extra check to limit access to hidden prfiles data in Launcher apps APIs." + description: "Enable extra check to limit access to hidden profiles data in Launcher apps APIs." bug: "321988638" } + +flag { + name: "reorder_wallpaper_during_user_switch" + namespace: "multiuser" + description: "Reorder loading home and lock screen wallpapers during a user switch." + bug: "324911115" +} + +flag { + name: "set_power_mode_during_user_switch" + namespace: "multiuser" + description: "Set power mode during a user switch." + bug: "325249845" +} diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 870546a6fd2b..ba356bb55194 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -40,7 +40,7 @@ import dalvik.system.CloseGuard; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.CursorWindow_host") + "com.android.platform.test.ravenwood.nativesubstitution.CursorWindow_host") public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; diff --git a/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl b/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl new file mode 100644 index 000000000000..838e41ee1c08 --- /dev/null +++ b/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +/** @hide */ +parcelable CameraPrivacyAllowlistEntry { + String packageName; + boolean isMandatory; +} + diff --git a/core/java/android/hardware/ISensorPrivacyListener.aidl b/core/java/android/hardware/ISensorPrivacyListener.aidl index 2ac21d2615aa..19ae302355be 100644 --- a/core/java/android/hardware/ISensorPrivacyListener.aidl +++ b/core/java/android/hardware/ISensorPrivacyListener.aidl @@ -25,5 +25,6 @@ oneway interface ISensorPrivacyListener { // frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl // =============== Beginning of transactions used on native side as well ====================== void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled); + void onSensorPrivacyStateChanged(int toggleType, int sensor, int state); // =============== End of transactions used on native side as well ============================ } diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl index 9cf329ca3d3d..851ce2add94f 100644 --- a/core/java/android/hardware/ISensorPrivacyManager.aidl +++ b/core/java/android/hardware/ISensorPrivacyManager.aidl @@ -16,6 +16,7 @@ package android.hardware; +import android.hardware.CameraPrivacyAllowlistEntry; import android.hardware.ISensorPrivacyListener; /** @hide */ @@ -45,6 +46,22 @@ interface ISensorPrivacyManager { void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable); void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)") + List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist(); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)") + int getToggleSensorPrivacyState(int toggleType, int sensor); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)") + void setToggleSensorPrivacyState(int userId, int source, int sensor, int state); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)") + void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, int state); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)") + boolean isCameraPrivacyEnabled(String packageName); + // =============== End of transactions used on native side as well ============================ void suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token, @@ -53,4 +70,4 @@ interface ISensorPrivacyManager { boolean requiresAuthentication(); void showSensorUseDialog(int sensor); -}
\ No newline at end of file +} diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java index 72586b2b22cb..88089eed50f4 100644 --- a/core/java/android/hardware/OverlayProperties.java +++ b/core/java/android/hardware/OverlayProperties.java @@ -71,17 +71,36 @@ public final class OverlayProperties implements Parcelable { /** * @return True if the device can support fp16, false otherwise. + * TODO: Move this to isCombinationSupported once the flag flips * @hide */ public boolean isFp16SupportedForHdr() { if (mNativeObject == 0) { return false; } - return nSupportFp16ForHdr(mNativeObject); + return nIsCombinationSupported( + mNativeObject, DataSpace.DATASPACE_SCRGB, HardwareBuffer.RGBA_FP16); } /** - * Indicates that hw composition of two or more overlays + * Indicates that hardware composition of a buffer encoded with the provided {@link DataSpace} + * and {@link HardwareBuffer.Format} is supported on the device. + * + * @return True if the device can support efficiently compositing the content described by the + * dataspace and format. False if GPOU composition fallback is otherwise required. + */ + @FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API) + public boolean isCombinationSupported(@DataSpace.ColorDataSpace int dataspace, + @HardwareBuffer.Format int format) { + if (mNativeObject == 0) { + return false; + } + + return nIsCombinationSupported(mNativeObject, dataspace, format); + } + + /** + * Indicates that hardware composition of two or more overlays * with different colorspaces is supported on the device. * * @return True if the device can support mixed colorspaces efficiently, @@ -131,6 +150,8 @@ public final class OverlayProperties implements Parcelable { private static native long nCreateDefault(); private static native boolean nSupportFp16ForHdr(long nativeObject); private static native boolean nSupportMixedColorSpaces(long nativeObject); + private static native boolean nIsCombinationSupported( + long nativeObject, int dataspace, int format); private static native void nWriteOverlayPropertiesToParcel(long nativeObject, Parcel dest); private static native long nReadOverlayPropertiesFromParcel(Parcel in); } diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index 18c95bfbb297..6294a8d617de 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -17,6 +17,7 @@ package android.hardware; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -38,9 +39,11 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.GuardedBy; +import com.android.internal.camera.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; @@ -215,13 +218,41 @@ public final class SensorPrivacyManager { public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED; /** + * Constant indicating privacy is enabled except for the automotive driver assistance apps + * which are helpful for driving. + */ + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = + SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS; + + /** + * Constant indicating privacy is enabled except for the automotive driver assistance apps + * which are required by car manufacturer for driving. + */ + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = + SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS; + + /** + * Constant indicating privacy is enabled except for the automotive driver assistance apps + * which are both helpful for driving and also apps required by car manufacturer for + * driving. + */ + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = + SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_APPS; + + /** * Types of state which can exist for a sensor privacy toggle * * @hide */ @IntDef(value = { ENABLED, - DISABLED + DISABLED, + AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS, + AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS, + AUTOMOTIVE_DRIVER_ASSISTANCE_APPS }) @Retention(RetentionPolicy.SOURCE) public @interface StateType {} @@ -266,6 +297,19 @@ public final class SensorPrivacyManager { private int mToggleType; private int mSensor; private boolean mEnabled; + private int mState; + + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private SensorPrivacyChangedParams(int toggleType, int sensor, int state) { + mToggleType = toggleType; + mSensor = sensor; + mState = state; + if (state == StateTypes.ENABLED) { + mEnabled = true; + } else { + mEnabled = false; + } + } private SensorPrivacyChangedParams(int toggleType, int sensor, boolean enabled) { mToggleType = toggleType; @@ -284,6 +328,12 @@ public final class SensorPrivacyManager { public boolean isEnabled() { return mEnabled; } + + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public @StateTypes.StateType int getState() { + return mState; + } + } } @@ -319,6 +369,9 @@ public final class SensorPrivacyManager { private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>, OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private ArrayMap<String, Boolean> mCameraPrivacyAllowlist = null; + /** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local * listeners */ @NonNull @@ -328,12 +381,33 @@ public final class SensorPrivacyManager { synchronized (mLock) { for (int i = 0; i < mToggleListeners.size(); i++) { OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i); + if (Flags.cameraPrivacyAllowlist()) { + int state = enabled ? StateTypes.ENABLED : StateTypes.DISABLED; + mToggleListeners.valueAt(i).execute(() -> listener + .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener + .SensorPrivacyChangedParams(toggleType, sensor, state))); + } else { + mToggleListeners.valueAt(i).execute(() -> listener + .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener + .SensorPrivacyChangedParams(toggleType, sensor, enabled))); + } + } + } + } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void onSensorPrivacyStateChanged(int toggleType, int sensor, int state) { + synchronized (mLock) { + for (int i = 0; i < mToggleListeners.size(); i++) { + OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i); mToggleListeners.valueAt(i).execute(() -> listener .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener - .SensorPrivacyChangedParams(toggleType, sensor, enabled))); + .SensorPrivacyChangedParams(toggleType, sensor, state))); } } } + }; /** Whether the singleton ISensorPrivacyListener has been registered */ @@ -649,6 +723,73 @@ public final class SensorPrivacyManager { } /** + * Returns sensor privacy state for a specific sensor. + * + * @return int sensor privacy state. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public @StateTypes.StateType int getSensorPrivacyState(@ToggleType int toggleType, + @Sensors.Sensor int sensor) { + try { + return mService.getToggleSensorPrivacyState(toggleType, sensor); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns if camera privacy is enabled for a specific package. + * + * @return boolean sensor privacy state. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public boolean isCameraPrivacyEnabled(@NonNull String packageName) { + try { + return mService.isCameraPrivacyEnabled(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns camera privacy allowlist. + * + * @return List of automotive driver assistance packages for + * privacy allowlisting. The returned map includes the package + * name as key and the value is a Boolean which tells if that package + * is required by the car manufacturer as mandatory package for driving. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public @NonNull Map<String, Boolean> getCameraPrivacyAllowlist() { + synchronized (mLock) { + if (mCameraPrivacyAllowlist == null) { + mCameraPrivacyAllowlist = new ArrayMap<>(); + try { + for (CameraPrivacyAllowlistEntry entry : + mService.getCameraPrivacyAllowlist()) { + mCameraPrivacyAllowlist.put(entry.packageName, entry.isMandatory); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return mCameraPrivacyAllowlist; + } + } + + /** * Sets sensor privacy to the specified state for an individual sensor. * * @param sensor the sensor which to change the state for @@ -677,6 +818,22 @@ public final class SensorPrivacyManager { * Sets sensor privacy to the specified state for an individual sensor. * * @param sensor the sensor which to change the state for + * @param state the state to which sensor privacy should be set. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void setSensorPrivacyState(@Sensors.Sensor int sensor, + @StateTypes.StateType int state) { + setSensorPrivacyState(resolveSourceFromCurrentContext(), sensor, state); + } + + /** + * Sets sensor privacy to the specified state for an individual sensor. + * + * @param sensor the sensor which to change the state for * @param enable the state to which sensor privacy should be set. * * @hide @@ -708,6 +865,27 @@ public final class SensorPrivacyManager { } /** + * Sets sensor privacy to the specified state for an individual sensor. + * + * @param sensor the sensor which to change the state for + * @param state the state to which sensor privacy should be set. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void setSensorPrivacyState(@Sources.Source int source, @Sensors.Sensor int sensor, + @StateTypes.StateType int state) { + try { + mService.setToggleSensorPrivacyState(mContext.getUserId(), source, sensor, state); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + } + + /** * Sets sensor privacy to the specified state for an individual sensor for the profile group of * context's user. * @@ -745,6 +923,28 @@ public final class SensorPrivacyManager { } /** + * Sets sensor privacy to the specified state for an individual sensor for the profile group of + * context's user. + * + * @param source the source using which the sensor is toggled. + * @param sensor the sensor which to change the state for + * @param state the state to which sensor privacy should be set. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void setSensorPrivacyStateForProfileGroup(@Sources.Source int source, + @Sensors.Sensor int sensor, @StateTypes.StateType int state) { + try { + mService.setToggleSensorPrivacyStateForProfileGroup(mContext.getUserId(), source, + sensor, state); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Don't show dialogs to turn off sensor privacy for this package. * * @param suppress Whether to suppress or re-enable. @@ -865,6 +1065,12 @@ public final class SensorPrivacyManager { boolean enabled) { listener.onAllSensorPrivacyChanged(enabled); } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void onSensorPrivacyStateChanged(int toggleType, int sensor, + int state) { + } }; mListeners.put(listener, iListener); } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 451d6fba0105..2add77e44dd3 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.DeviceStateSensorOrientationMap; @@ -6076,6 +6077,28 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS_MAXIMUM_RESOLUTION = new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.jpegr.availableJpegRStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class); + /** + * <p>Minimum and maximum padding zoom factors supported by this camera device for + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension.</p> + * <p>The minimum and maximum padding zoom factors supported by the device for + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension feature. This extension specific camera characteristic can be queried using + * {@link android.hardware.camera2.CameraExtensionCharacteristics#get }.</p> + * <p><b>Units</b>: A pair of padding zoom factors in floating-points: + * (minPaddingZoomFactor, maxPaddingZoomFactor)</p> + * <p><b>Range of valid values:</b><br></p> + * <p>1.0 < minPaddingZoomFactor <= maxPaddingZoomFactor</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE = + new Key<android.util.Range<Float>>("android.efv.paddingZoomFactorRange", new TypeReference<android.util.Range<Float>>() {{ }}); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 3b10e0dd516a..76c20ce2184a 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics.Key; import android.hardware.camera2.extension.IAdvancedExtenderImpl; import android.hardware.camera2.extension.ICameraExtensionsProxyService; import android.hardware.camera2.extension.IImageCaptureExtenderImpl; @@ -35,6 +36,8 @@ import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.SizeList; import android.hardware.camera2.impl.CameraExtensionUtils; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.impl.ExtensionKey; +import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.os.Binder; @@ -805,13 +808,11 @@ public final class CameraExtensionCharacteristics { extender.init(mCameraId, mCharacteristicsMapNative); CameraMetadataNative metadata = extender.getAvailableCharacteristicsKeyValues(mCameraId); - CameraCharacteristics fallbackCharacteristics = mCharacteristicsMap.get(mCameraId); if (metadata == null) { - return fallbackCharacteristics.get(key); + return null; } CameraCharacteristics characteristics = new CameraCharacteristics(metadata); - T value = characteristics.get(key); - return value == null ? fallbackCharacteristics.get(key) : value; + return characteristics.get(key); } } catch (RemoteException e) { Log.e(TAG, "Failed to query the extension for the specified key! Extension " @@ -1497,4 +1498,28 @@ public final class CameraExtensionCharacteristics { return Collections.unmodifiableSet(ret); } + + + /** + * <p>Minimum and maximum padding zoom factors supported by this camera device for + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for + * the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension.</p> + * <p>The minimum and maximum padding zoom factors supported by the device for + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension feature. This extension specific camera characteristic can be queried using + * {@link android.hardware.camera2.CameraExtensionCharacteristics#get}.</p> + * <p><b>Units</b>: A pair of padding zoom factors in floating-points: + * (minPaddingZoomFactor, maxPaddingZoomFactor)</p> + * <p><b>Range of valid values:</b><br></p> + * <p>1.0 < minPaddingZoomFactor <= maxPaddingZoomFactor</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE = + CameraCharacteristics.EFV_PADDING_ZOOM_FACTOR_RANGE; } diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java index 21fead980cdd..2d9433e31ab2 100644 --- a/core/java/android/hardware/camera2/CameraExtensionSession.java +++ b/core/java/android/hardware/camera2/CameraExtensionSession.java @@ -16,11 +16,14 @@ package android.hardware.camera2; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.camera2.utils.HashCodeHelpers; +import com.android.internal.camera.flags.Flags; + import java.util.concurrent.Executor; /** @@ -132,6 +135,34 @@ public abstract class CameraExtensionSession implements AutoCloseable { } /** + * This method is called instead of + * {@link #onCaptureProcessStarted} when the camera device failed + * to produce the required input for the device-specific extension. The + * cause could be a failed camera capture request, a failed + * capture result or dropped camera frame. More information about + * the reason is included in the 'failure' argument. + * + * <p>Other requests are unaffected, and some or all image buffers + * from the capture may have been pushed to their respective output + * streams.</p> + * + * <p>The default implementation of this method does nothing.</p> + * + * @param session the session received during + * {@link StateCallback#onConfigured(CameraExtensionSession)} + * @param request The request that was given to the CameraDevice + * @param failure The capture failure reason + * + * @see #capture + * @see #setRepeatingRequest + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public void onCaptureFailed(@NonNull CameraExtensionSession session, + @NonNull CaptureRequest request, @CaptureFailure.FailureReason int failure) { + // default empty implementation + } + + /** * This method is called independently of the others in * ExtensionCaptureCallback, when a capture sequence finishes. * diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 7cf10d89004f..e24c98e98c5d 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.util.Log; @@ -276,8 +277,11 @@ public abstract class CameraMetadata<TKey> { throw new IllegalArgumentException("key type must be that of a metadata key"); } - if (field.getAnnotation(PublicKey.class) == null) { - // Never expose @hide keys up to the API user + if (field.getAnnotation(PublicKey.class) == null + && field.getAnnotation(ExtensionKey.class) == null) { + // Never expose @hide keys to the API user unless they are + // marked as @ExtensionKey, as these keys are publicly accessible via + // the extension key classes. return false; } @@ -3893,6 +3897,36 @@ public abstract class CameraMetadata<TKey> { public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // + // Enumeration values for CaptureRequest#EFV_STABILIZATION_MODE + // + + /** + * <p>No stabilization.</p> + * @see CaptureRequest#EFV_STABILIZATION_MODE + * @hide + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_OFF = 0; + + /** + * <p>Gimbal stabilization mode.</p> + * @see CaptureRequest#EFV_STABILIZATION_MODE + * @hide + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_GIMBAL = 1; + + /** + * <p>Locked stabilization mode which uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization to directionally steady the target region.</p> + * @see CaptureRequest#EFV_STABILIZATION_MODE + * @hide + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_LOCKED = 2; + + // // Enumeration values for CaptureResult#CONTROL_AE_STATE // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index ded96a23e11e..66efccd14097 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.OutputConfiguration; @@ -4292,6 +4293,146 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> public static final Key<Integer> EXTENSION_STRENGTH = new Key<Integer>("android.extension.strength", int.class); + /** + * <p>Used to apply an additional digital zoom factor for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. + * This additional zoom factor serves as a buffer to provide more flexibility for the + * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } + * mode. If android.efv.paddingZoomFactor is not set, the default will be used. + * The effectiveness of the stabilization may be influenced by the amount of padding zoom + * applied. A higher padding zoom factor can stabilize the target region more effectively + * with greater flexibility but may potentially impact image quality. Conversely, a lower + * padding zoom factor may be used to prioritize preserving image quality, albeit with less + * leeway in stabilizing the target region. It is recommended to set the + * android.efv.paddingZoomFactor to at least 1.5.</p> + * <p>If android.efv.autoZoom is enabled, the requested android.efv.paddingZoomFactor will be overridden. + * android.efv.maxPaddingZoomFactor can be checked for more details on controlling the + * padding zoom factor during android.efv.autoZoom.</p> + * <p><b>Range of valid values:</b><br> + * android.efv.paddingZoomFactorRange</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = + new Key<Float>("android.efv.paddingZoomFactor", float.class); + + /** + * <p>Used to enable or disable auto zoom for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Turn on auto zoom to let the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature decide at any given point a combination of + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and android.efv.paddingZoomFactor + * to keep the target region in view and stabilized. The combination chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested + * android.efv.paddingZoomFactor. A limit can be set on the padding zoom if wanting + * to control image quality further using android.efv.maxPaddingZoomFactor.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Boolean> EFV_AUTO_ZOOM = + new Key<Boolean>("android.efv.autoZoom", boolean.class); + + /** + * <p>Used to limit the android.efv.paddingZoomFactor if + * android.efv.autoZoom is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>If android.efv.autoZoom is enabled, this key can be used to set a limit + * on the android.efv.paddingZoomFactor chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode + * to control image quality.</p> + * <p><b>Range of valid values:</b><br> + * The range of android.efv.paddingZoomFactorRange. Use a value greater than or equal to + * the android.efv.paddingZoomFactor to effectively utilize this key.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = + new Key<Float>("android.efv.maxPaddingZoomFactor", float.class); + + /** + * <p>Set the stabilization mode for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension</p> + * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked + * video stabilization. Locked mode uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization feature to fixate on the current region, utilizing it as the target area for + * stabilization.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li> + * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li> + * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #EFV_STABILIZATION_MODE_OFF + * @see #EFV_STABILIZATION_MODE_GIMBAL + * @see #EFV_STABILIZATION_MODE_LOCKED + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Integer> EFV_STABILIZATION_MODE = + new Key<Integer>("android.efv.stabilizationMode", int.class); + + /** + * <p>Used to update the target region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A android.util.Pair<Integer,Integer> that represents the desired + * <Horizontal,Vertical> shift of the current locked view (or target region) in + * pixels. Negative values indicate left and upward shifts, while positive values indicate + * right and downward shifts in the active array coordinate system.</p> + * <p><b>Range of valid values:</b><br> + * android.util.Pair<Integer,Integer> represents the + * <Horizontal,Vertical> shift. The range for the horizontal shift is + * [-max(android.efv.paddingRegion-left), max(android.efv.paddingRegion-right)]. + * The range for the vertical shift is + * [-max(android.efv.paddingRegion-top), max(android.efv.paddingRegion-bottom)]</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = + new Key<android.util.Pair<Integer,Integer>>("android.efv.translateViewport", new TypeReference<android.util.Pair<Integer,Integer>>() {{ }}); + + /** + * <p>Representing the desired clockwise rotation + * of the target region in degrees for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Value representing the desired clockwise rotation of the target + * region in degrees.</p> + * <p><b>Range of valid values:</b><br> + * 0 to 360</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_ROTATE_VIEWPORT = + new Key<Float>("android.efv.rotateViewport", float.class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 7cf5a7f2e445..a01c23d984f4 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; +import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.utils.TypeReference; @@ -5919,6 +5920,214 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { public static final Key<Integer> EXTENSION_STRENGTH = new Key<Integer>("android.extension.strength", int.class); + /** + * <p>The padding region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides + * before the target region starts to go out of bounds.</p> + * <p>The padding region denotes the area surrounding the stabilized target region within which + * the camera can be moved while maintaining the target region in view. As the camera moves, + * the padding region adjusts to represent the proximity of the target region to the + * boundary, which is the point at which the target region will start to go out of bounds.</p> + * <p><b>Range of valid values:</b><br> + * The padding is the number of remaining pixels of padding in each direction. + * The pixels reference the active array coordinate system. Negative values indicate the target + * region is out of bounds. The value for this key may be null for when the stabilization mode is + * in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_OFF } + * or {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_GIMBAL } mode.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<int[]> EFV_PADDING_REGION = + new Key<int[]>("android.efv.paddingRegion", int[].class); + + /** + * <p>The padding region when android.efv.autoZoom is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides + * before the target region starts to go out of bounds.</p> + * <p>This may differ from android.efv.paddingRegion as the field of view can change + * during android.efv.autoZoom, altering the boundary region and thus updating the padding between the + * target region and the boundary.</p> + * <p><b>Range of valid values:</b><br> + * The padding is the number of remaining pixels of padding in each direction + * when android.efv.autoZoom is enabled. Negative values indicate the target region is out of bounds. + * The value for this key may be null for when the android.efv.autoZoom is not enabled.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION = + new Key<int[]>("android.efv.autoZoomPaddingRegion", int[].class); + + /** + * <p>List of coordinates representing the target region relative to the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE } + * for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in + * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A list of android.graphics.PointF that define the coordinates of the target region + * relative to the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }. + * The array represents the target region coordinates as: top-left, top-right, bottom-left, + * bottom-right.</p> + * <p><b>Range of valid values:</b><br> + * The list of target coordinates will define a region within the bounds of the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES = + new Key<android.graphics.PointF[]>("android.efv.targetCoordinates", android.graphics.PointF[].class); + + /** + * <p>Used to apply an additional digital zoom factor for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. + * This additional zoom factor serves as a buffer to provide more flexibility for the + * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } + * mode. If android.efv.paddingZoomFactor is not set, the default will be used. + * The effectiveness of the stabilization may be influenced by the amount of padding zoom + * applied. A higher padding zoom factor can stabilize the target region more effectively + * with greater flexibility but may potentially impact image quality. Conversely, a lower + * padding zoom factor may be used to prioritize preserving image quality, albeit with less + * leeway in stabilizing the target region. It is recommended to set the + * android.efv.paddingZoomFactor to at least 1.5.</p> + * <p>If android.efv.autoZoom is enabled, the requested android.efv.paddingZoomFactor will be overridden. + * android.efv.maxPaddingZoomFactor can be checked for more details on controlling the + * padding zoom factor during android.efv.autoZoom.</p> + * <p><b>Range of valid values:</b><br> + * android.efv.paddingZoomFactorRange</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = + new Key<Float>("android.efv.paddingZoomFactor", float.class); + + /** + * <p>Set the stabilization mode for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension</p> + * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked + * video stabilization. Locked mode uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization feature to fixate on the current region, utilizing it as the target area for + * stabilization.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li> + * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li> + * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #EFV_STABILIZATION_MODE_OFF + * @see #EFV_STABILIZATION_MODE_GIMBAL + * @see #EFV_STABILIZATION_MODE_LOCKED + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Integer> EFV_STABILIZATION_MODE = + new Key<Integer>("android.efv.stabilizationMode", int.class); + + /** + * <p>Used to enable or disable auto zoom for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Turn on auto zoom to let the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature decide at any given point a combination of + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and android.efv.paddingZoomFactor + * to keep the target region in view and stabilized. The combination chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested + * android.efv.paddingZoomFactor. A limit can be set on the padding zoom if wanting + * to control image quality further using android.efv.maxPaddingZoomFactor.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Boolean> EFV_AUTO_ZOOM = + new Key<Boolean>("android.efv.autoZoom", boolean.class); + + /** + * <p>Representing the desired clockwise rotation + * of the target region in degrees for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Value representing the desired clockwise rotation of the target + * region in degrees.</p> + * <p><b>Range of valid values:</b><br> + * 0 to 360</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_ROTATE_VIEWPORT = + new Key<Float>("android.efv.rotateViewport", float.class); + + /** + * <p>Used to update the target region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A android.util.Pair<Integer,Integer> that represents the desired + * <Horizontal,Vertical> shift of the current locked view (or target region) in + * pixels. Negative values indicate left and upward shifts, while positive values indicate + * right and downward shifts in the active array coordinate system.</p> + * <p><b>Range of valid values:</b><br> + * android.util.Pair<Integer,Integer> represents the + * <Horizontal,Vertical> shift. The range for the horizontal shift is + * [-max(android.efv.paddingRegion-left), max(android.efv.paddingRegion-right)]. + * The range for the vertical shift is + * [-max(android.efv.paddingRegion-top), max(android.efv.paddingRegion-bottom)]</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = + new Key<android.util.Pair<Integer,Integer>>("android.efv.translateViewport", new TypeReference<android.util.Pair<Integer,Integer>>() {{ }}); + + /** + * <p>Used to limit the android.efv.paddingZoomFactor if + * android.efv.autoZoom is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>If android.efv.autoZoom is enabled, this key can be used to set a limit + * on the android.efv.paddingZoomFactor chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode + * to control image quality.</p> + * <p><b>Range of valid values:</b><br> + * The range of android.efv.paddingZoomFactorRange. Use a value greater than or equal to + * the android.efv.paddingZoomFactor to effectively utilize this key.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = + new Key<Float>("android.efv.maxPaddingZoomFactor", float.class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java new file mode 100644 index 000000000000..32039c6ec0ba --- /dev/null +++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureRequest.Key; +import android.hardware.camera2.impl.ExtensionKey; +import android.hardware.camera2.impl.PublicKey; + +import com.android.internal.camera.flags.Flags; + +/** + * ExtensionCaptureRequest contains definitions for extension-specific CaptureRequest keys that + * can be used to configure a {@link android.hardware.camera2.CaptureRequest} during a + * {@link android.hardware.camera2.CameraExtensionSession}. + * + * Note that ExtensionCaptureRequest is not intended to be used as a replacement + * for CaptureRequest in the extensions. It serves as a supplementary class providing + * extension-specific CaptureRequest keys. Developers should use these keys in conjunction + * with regular CaptureRequest objects during a + * {@link android.hardware.camera2.CameraExtensionSession}. + * + * @see CaptureRequest + * @see CameraExtensionSession + */ +@FlaggedApi(Flags.FLAG_CONCERT_MODE) +public final class ExtensionCaptureRequest { + + /** + * <p>Used to apply an additional digital zoom factor for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. + * This additional zoom factor serves as a buffer to provide more flexibility for the + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } + * mode. If {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } is not set, the default will be used. + * The effectiveness of the stabilization may be influenced by the amount of padding zoom + * applied. A higher padding zoom factor can stabilize the target region more effectively + * with greater flexibility but may potentially impact image quality. Conversely, a lower + * padding zoom factor may be used to prioritize preserving image quality, albeit with less + * leeway in stabilizing the target region. It is recommended to set the + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to at least 1.5.</p> + * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, the requested {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } will be overridden. + * {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR } can be checked for more details on controlling the + * padding zoom factor during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }.</p> + * <p><b>Range of valid values:</b><br> + * {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM + * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = CaptureRequest.EFV_PADDING_ZOOM_FACTOR; + + /** + * <p>Used to enable or disable auto zoom for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Turn on auto zoom to let the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature decide at any given point a combination of + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } + * to keep the target region in view and stabilized. The combination chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested + * {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }. A limit can be set on the padding zoom if wanting + * to control image quality further using {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR }.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Boolean> EFV_AUTO_ZOOM = CaptureRequest.EFV_AUTO_ZOOM; + + /** + * <p>Used to limit the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } if + * {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, this key can be used to set a limit + * on the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode + * to control image quality.</p> + * <p><b>Range of valid values:</b><br> + * The range of {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE Range}. Use a value greater than or equal to + * the {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to + * effectively utilize this key.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = CaptureRequest.EFV_MAX_PADDING_ZOOM_FACTOR; + + /** + * <p>Set the stabilization mode for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension</p> + * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked + * video stabilization. Locked mode uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization feature to fixate on the current region, utilizing it as the target area for + * stabilization.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li> + * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li> + * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #EFV_STABILIZATION_MODE_OFF + * @see #EFV_STABILIZATION_MODE_GIMBAL + * @see #EFV_STABILIZATION_MODE_LOCKED + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Integer> EFV_STABILIZATION_MODE = CaptureRequest.EFV_STABILIZATION_MODE; + + /** + * <p>Used to update the target region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A android.util.Pair<Integer,Integer> that represents the desired + * <Horizontal,Vertical> shift of the current locked view (or target region) in + * pixels. Negative values indicate left and upward shifts, while positive values indicate + * right and downward shifts in the active array coordinate system.</p> + * <p><b>Range of valid values:</b><br> + * android.util.Pair<Integer,Integer> represents the + * <Horizontal,Vertical> shift. The range for the horizontal shift is + * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-left), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-right)]. + * The range for the vertical shift is + * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-top), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-bottom)]</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see ExtensionCaptureResult#EFV_PADDING_REGION + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = CaptureRequest.EFV_TRANSLATE_VIEWPORT; + + /** + * <p>Representing the desired clockwise rotation + * of the target region in degrees for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Value representing the desired clockwise rotation of the target + * region in degrees.</p> + * <p><b>Range of valid values:</b><br> + * 0 to 360</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_ROTATE_VIEWPORT = CaptureRequest.EFV_ROTATE_VIEWPORT; + + + // + // Enumeration values for CaptureRequest#EFV_STABILIZATION_MODE + // + + /** + * <p>No stabilization.</p> + * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_OFF = CaptureRequest.EFV_STABILIZATION_MODE_OFF; + + /** + * <p>Gimbal stabilization mode.</p> + * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_GIMBAL = CaptureRequest.EFV_STABILIZATION_MODE_GIMBAL; + + /** + * <p>Locked stabilization mode which uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization to directionally steady the target region.</p> + * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE + */ + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final int EFV_STABILIZATION_MODE_LOCKED = CaptureRequest.EFV_STABILIZATION_MODE_LOCKED; + +}
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java new file mode 100644 index 000000000000..5c9990975a9b --- /dev/null +++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.CaptureResult.Key; +import android.hardware.camera2.impl.ExtensionKey; +import android.hardware.camera2.impl.PublicKey; + +import com.android.internal.camera.flags.Flags; + +/** + * ExtensionCaptureResult contains definitions for extension-specific CaptureResult keys that + * are available during a {@link android.hardware.camera2.CameraExtensionSession} after a + * {@link android.hardware.camera2.CaptureRequest} is processed. + * + * Note that ExtensionCaptureResult is not intended to be used as a replacement + * for CaptureResult in the extensions. It serves as a supplementary class providing + * extension-specific CaptureResult keys. Developers should use these keys in conjunction + * with regular CaptureResult objects during a + * {@link android.hardware.camera2.CameraExtensionSession}. + * + * @see CaptureResult + * @see CaptureRequest + * @see CameraExtensionSession + */ +@FlaggedApi(Flags.FLAG_CONCERT_MODE) +public final class ExtensionCaptureResult { + + /** + * <p>The padding region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides + * before the target region starts to go out of bounds.</p> + * <p>The padding region denotes the area surrounding the stabilized target region within which + * the camera can be moved while maintaining the target region in view. As the camera moves, + * the padding region adjusts to represent the proximity of the target region to the + * boundary, which is the point at which the target region will start to go out of bounds.</p> + * <p><b>Range of valid values:</b><br> + * The padding is the number of remaining pixels of padding in each direction. + * The pixels reference the active array coordinate system. Negative values indicate the target region + * is out of bounds. The value for this key may be null for when the stabilization mode is + * in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF } + * or {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL } mode.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<int[]> EFV_PADDING_REGION = CaptureResult.EFV_PADDING_REGION; + + /** + * <p>The padding region when {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides + * before the target region starts to go out of bounds.</p> + * <p>This may differ from {@link ExtensionCaptureResult#EFV_PADDING_REGION } as the field of view can change + * during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }, altering the boundary region and thus updating the padding between the + * target region and the boundary.</p> + * <p><b>Range of valid values:</b><br> + * The padding is the number of remaining pixels of padding in each direction + * when {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled. Negative values indicate the target region is out of bounds. + * The value for this key may be null for when the {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is not enabled.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM + * @see ExtensionCaptureResult#EFV_PADDING_REGION + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION = CaptureResult.EFV_AUTO_ZOOM_PADDING_REGION; + + /** + * <p>List of coordinates representing the target region relative to the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE } + * for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A list of android.graphics.PointF that define the coordinates of the target region + * relative to the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }. + * The array represents the target region coordinates as: top-left, top-right, bottom-left, + * bottom-right.</p> + * <p><b>Range of valid values:</b><br> + * The list of target coordinates will define a region within the bounds of the + * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES = CaptureResult.EFV_TARGET_COORDINATES; + + /** + * <p>Used to apply an additional digital zoom factor for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. + * This additional zoom factor serves as a buffer to provide more flexibility for the + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } + * mode. If {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } is not set, the default will be used. + * The effectiveness of the stabilization may be influenced by the amount of padding zoom + * applied. A higher padding zoom factor can stabilize the target region more effectively + * with greater flexibility but may potentially impact image quality. Conversely, a lower + * padding zoom factor may be used to prioritize preserving image quality, albeit with less + * leeway in stabilizing the target region. It is recommended to set the + * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to at least 1.5.</p> + * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, the requested {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } will be overridden. + * {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR } can be checked for more details on controlling the + * padding zoom factor during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }.</p> + * <p><b>Range of valid values:</b><br> + * {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM + * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = CaptureResult.EFV_PADDING_ZOOM_FACTOR; + + /** + * <p>Set the stabilization mode for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension</p> + * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked + * video stabilization. Locked mode uses the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * stabilization feature to fixate on the current region, utilizing it as the target area for + * stabilization.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li> + * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li> + * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #EFV_STABILIZATION_MODE_OFF + * @see #EFV_STABILIZATION_MODE_GIMBAL + * @see #EFV_STABILIZATION_MODE_LOCKED + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Integer> EFV_STABILIZATION_MODE = CaptureResult.EFV_STABILIZATION_MODE; + + /** + * <p>Used to enable or disable auto zoom for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Turn on auto zoom to let the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * feature decide at any given point a combination of + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } + * to keep the target region in view and stabilized. The combination chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested + * {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }. A limit can be set on the padding zoom if wanting + * to control image quality further using {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR }.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Boolean> EFV_AUTO_ZOOM = CaptureResult.EFV_AUTO_ZOOM; + + /** + * <p>Representing the desired clockwise rotation + * of the target region in degrees for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>Value representing the desired clockwise rotation of the target + * region in degrees.</p> + * <p><b>Range of valid values:</b><br> + * 0 to 360</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_ROTATE_VIEWPORT = CaptureResult.EFV_ROTATE_VIEWPORT; + + /** + * <p>Used to update the target region for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>A android.util.Pair<Integer,Integer> that represents the desired + * <Horizontal,Vertical> shift of the current locked view (or target region) in + * pixels. Negative values indicate left and upward shifts, while positive values indicate + * right and downward shifts in the active array coordinate system.</p> + * <p><b>Range of valid values:</b><br> + * android.util.Pair<Integer,Integer> represents the + * <Horizontal,Vertical> shift. The range for the horizontal shift is + * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-left), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-right)]. + * The range for the vertical shift is + * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-top), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-bottom)]</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see ExtensionCaptureResult#EFV_PADDING_REGION + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = CaptureResult.EFV_TRANSLATE_VIEWPORT; + + /** + * <p>Used to limit the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } if + * {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p> + * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, this key can be used to set a limit + * on the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } chosen by the + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } + * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode + * to control image quality.</p> + * <p><b>Range of valid values:</b><br> + * The range of {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }. Use a value greater than or equal to + * the {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to + * effectively utilize this key.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM + * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR + * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE + */ + @PublicKey + @NonNull + @ExtensionKey + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = CaptureResult.EFV_MAX_PADDING_ZOOM_FACTOR; + +}
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java index b4fe7fe1f0d1..53f56bc9f896 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java +++ b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java @@ -18,8 +18,11 @@ package android.hardware.camera2.extension; import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.graphics.ImageFormat; +import android.hardware.camera2.params.ColorSpaceProfiles; +import android.hardware.camera2.params.DynamicRangeProfiles; import android.hardware.camera2.utils.SurfaceUtils; import android.util.Size; import android.view.Surface; @@ -65,6 +68,8 @@ public final class CameraOutputSurface { mOutputSurface.size = new android.hardware.camera2.extension.Size(); mOutputSurface.size.width = size.getWidth(); mOutputSurface.size.height = size.getHeight(); + mOutputSurface.dynamicRangeProfile = DynamicRangeProfiles.STANDARD; + mOutputSurface.colorSpace = ColorSpaceProfiles.UNSPECIFIED; } /** @@ -95,4 +100,48 @@ public final class CameraOutputSurface { public @ImageFormat.Format int getImageFormat() { return mOutputSurface.imageFormat; } + + /** + * Return the dynamic range profile. The default + * dynamicRangeProfile is + * {@link android.hardware.camera2.params.DynamicRangeProfiles.STANDARD} + * unless specified by CameraOutputSurface.setDynamicRangeProfile. + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public @DynamicRangeProfiles.Profile long getDynamicRangeProfile() { + return mOutputSurface.dynamicRangeProfile; + } + + /** + * Return the color space. The default colorSpace is + * {@link android.hardware.camera2.params.ColorSpaceProfiles.UNSPECIFIED} + * unless specified by CameraOutputSurface.setColorSpace. + */ + @SuppressLint("MethodNameUnits") + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public int getColorSpace() { + return mOutputSurface.colorSpace; + } + + /** + * Set the dynamic range profile. The default dynamicRangeProfile + * will be {@link android.hardware.camera2.params.DynamicRangeProfiles.STANDARD} + * unless explicitly set using this method. + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public void setDynamicRangeProfile( + @DynamicRangeProfiles.Profile long dynamicRangeProfile) { + mOutputSurface.dynamicRangeProfile = dynamicRangeProfile; + } + + /** + * Set the color space. The default colorSpace + * will be + * {@link android.hardware.camera2.params.ColorSpaceProfiles.UNSPECIFIED} + * unless explicitly set using this method. + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public void setColorSpace(int colorSpace) { + mOutputSurface.colorSpace = colorSpace; + } } diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl index 02a4690c3b77..b1f10ee103c1 100644 --- a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl +++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl @@ -15,6 +15,7 @@ */ package android.hardware.camera2.extension; +import android.hardware.camera2.extension.CaptureFailure; import android.hardware.camera2.extension.Request; import android.hardware.camera2.impl.CameraMetadataNative; @@ -28,4 +29,5 @@ interface ICaptureCallback void onCaptureSequenceAborted(int captureSequenceId); void onCaptureCompleted(long shutterTimestamp, int requestId, in CameraMetadataNative results); void onCaptureProcessProgressed(int progress); + void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason); } diff --git a/core/java/android/hardware/camera2/extension/OutputSurface.aidl b/core/java/android/hardware/camera2/extension/OutputSurface.aidl index 841537918f65..02e160cdd8dd 100644 --- a/core/java/android/hardware/camera2/extension/OutputSurface.aidl +++ b/core/java/android/hardware/camera2/extension/OutputSurface.aidl @@ -24,4 +24,6 @@ parcelable OutputSurface Surface surface; Size size; int imageFormat; + long dynamicRangeProfile; + int colorSpace; } diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java index e7cc5303fc18..d8594e5e1175 100644 --- a/core/java/android/hardware/camera2/extension/SessionProcessor.java +++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java @@ -18,8 +18,10 @@ package android.hardware.camera2.extension; import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.impl.CameraExtensionUtils.HandlerExecutor; @@ -132,13 +134,14 @@ public abstract class SessionProcessor { * This method is called instead of * {@link #onCaptureProcessStarted} when the camera device failed * to produce the required input for the device-specific - * extension. The cause could be a failed camera capture request, - * a failed capture result or dropped camera frame. + * extension. The callback allows clients to be notified + * about failure reason. * * @param captureSequenceId id of the current capture sequence + * @param failure The capture failure reason */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - void onCaptureFailed(int captureSequenceId); + void onCaptureFailed(int captureSequenceId, @CaptureFailure.FailureReason int failure); /** * This method is called independently of the others in the @@ -474,9 +477,9 @@ public abstract class SessionProcessor { } @Override - public void onCaptureFailed(int captureSequenceId) { + public void onCaptureFailed(int captureSequenceId, int failure) { try { - mCaptureCallback.onCaptureFailed(captureSequenceId); + mCaptureCallback.onCaptureProcessFailed(captureSequenceId, failure); } catch (RemoteException e) { Log.e(TAG, "Failed to notify capture failure start due to remote exception!"); } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index b2032fa3db81..a7d6caf9d9df 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -66,6 +66,8 @@ import android.util.Log; import android.util.Size; import android.view.Surface; +import com.android.internal.camera.flags.Flags; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -779,6 +781,22 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } @Override + public void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason) { + if (Flags.concertMode()) { + final long ident = Binder.clearCallingIdentity(); + try { + mClientExecutor.execute( + () -> mClientCallbacks.onCaptureFailed( + CameraAdvancedExtensionSessionImpl.this, mClientRequest, + captureFailureReason + )); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override public void onCaptureSequenceCompleted(int captureSequenceId) { final long ident = Binder.clearCallingIdentity(); try { diff --git a/core/java/android/hardware/camera2/impl/ExtensionKey.java b/core/java/android/hardware/camera2/impl/ExtensionKey.java new file mode 100644 index 000000000000..15e8982c12b4 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/ExtensionKey.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.camera2.impl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denote a static field {@code Key} as being an extension key (i.e. @hide as a CaptureRequest/ + * CaptureResult key but exposed as a @PublicKey through + * ExtensionCaptureRequest/ExtensionCaptureResult). + * + * <p>Keys with this annotation are assumed to always have a hidden key counter-part in + * CaptureRequest/CaptureResult.</p> + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ExtensionKey { + +} diff --git a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java index 0e6c1b39271c..69a6e9b0ab32 100644 --- a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java @@ -15,15 +15,20 @@ */ package android.hardware.camera2.params; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; - +import android.annotation.SuppressLint; +import android.graphics.ColorSpace; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraExtensionCharacteristics.Extension; import android.hardware.camera2.CameraExtensionSession; import java.util.List; import java.util.concurrent.Executor; +import com.android.internal.camera.flags.Flags; + /** * A class that aggregates all supported arguments for * {@link CameraExtensionSession} initialization. @@ -36,6 +41,7 @@ public final class ExtensionSessionConfiguration { private OutputConfiguration mPostviewOutput = null; private Executor mExecutor = null; private CameraExtensionSession.StateCallback mCallback = null; + private int mColorSpace; /** * Create a new ExtensionSessionConfiguration @@ -118,4 +124,55 @@ public final class ExtensionSessionConfiguration { Executor getExecutor() { return mExecutor; } + + /** + * Set a specific device-supported color space. + * + * <p>Clients can choose from any profile advertised as supported in + * {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES} + * queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}. + * When set, the colorSpace will override the default color spaces of the output targets, + * or the color space implied by the dataSpace passed into an {@link ImageReader}'s + * constructor.</p> + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public void setColorSpace(@NonNull ColorSpace.Named colorSpace) { + mColorSpace = colorSpace.ordinal(); + for (OutputConfiguration outputConfiguration : mOutputs) { + outputConfiguration.setColorSpace(colorSpace); + } + if (mPostviewOutput != null) { + mPostviewOutput.setColorSpace(colorSpace); + } + } + + /** + * Clear the color space, such that the default color space will be used. + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + public void clearColorSpace() { + mColorSpace = ColorSpaceProfiles.UNSPECIFIED; + for (OutputConfiguration outputConfiguration : mOutputs) { + outputConfiguration.clearColorSpace(); + } + if (mPostviewOutput != null) { + mPostviewOutput.clearColorSpace(); + } + } + + /** + * Return the current color space. + * + * @return the currently set color space, or null + * if not set + */ + @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT) + @SuppressLint("MethodNameUnits") + public @Nullable ColorSpace getColorSpace() { + if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) { + return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]); + } else { + return null; + } + } } diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java index f2ef185a0500..f7b417337911 100644 --- a/core/java/android/os/BugreportParams.java +++ b/core/java/android/os/BugreportParams.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.admin.flags.Flags; -import android.compat.annotation.UnsupportedAppUsage; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -127,12 +126,8 @@ public final class BugreportParams { /** * Options for a lightweight bugreport intended to be taken for onboarding-related flows. - * - * @hide */ - @TestApi @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED) - @UnsupportedAppUsage public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING; /** @@ -180,10 +175,7 @@ public final class BugreportParams { * The bugreport may be retrieved multiple times using * {@link BugreportManager#retrieveBugreport( * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}. - * - * @hide */ - @TestApi @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED) public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = IDumpstate.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL; diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index fbec518e4a29..3950c25675d8 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -42,7 +42,7 @@ import java.util.ArrayList; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.MessageQueue_host") + "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host") public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 8e860c35388d..ccfb6326d941 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -233,7 +233,8 @@ import java.util.function.IntFunction; * {@link #readSparseArray(ClassLoader, Class)}. */ @RavenwoodKeepWholeClass -@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.Parcel_host") +@RavenwoodNativeSubstitutionClass( + "com.android.platform.test.ravenwood.nativesubstitution.Parcel_host") public final class Parcel { private static final boolean DEBUG_RECYCLE = false; diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 6532d5c8784a..17dfdda7dffc 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -75,7 +75,8 @@ import java.nio.ByteOrder; * you to close it when done with it. */ @RavenwoodKeepWholeClass -@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.ParcelFileDescriptor_host") +@RavenwoodNativeSubstitutionClass( + "com.android.platform.test.ravenwood.nativesubstitution.ParcelFileDescriptor_host") public class ParcelFileDescriptor implements Parcelable, Closeable { private static final String TAG = "ParcelFileDescriptor"; diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index a818919d184e..0a386913de59 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -56,7 +56,8 @@ import java.util.function.Predicate; */ @SystemApi @RavenwoodKeepWholeClass -@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.SystemProperties_host") +@RavenwoodNativeSubstitutionClass( + "com.android.platform.test.ravenwood.nativesubstitution.SystemProperties_host") public class SystemProperties { private static final String TAG = "SystemProperties"; private static final boolean TRACK_KEY_ACCESS = false; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 89576ed62afe..2b30a2ba2d4e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -163,19 +163,16 @@ public class UserManager { * User type representing a managed profile, which is a profile that is to be managed by a * device policy controller (DPC). * The intended purpose is for work profiles, which are managed by a corporate entity. - * @hide */ - @SystemApi + @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE) public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED"; /** * User type representing a clone profile. Clone profile is a user profile type used to run * second instance of an otherwise single user App (eg, messengers). Currently only the * {@link android.content.pm.UserInfo#isMain()} user can have a clone profile. - * - * @hide */ - @SystemApi + @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE) public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE"; @@ -184,10 +181,8 @@ public class UserManager { * as an alternative user-space to install and use sensitive apps. * UI surfaces can adopt an alternative strategy to show apps belonging to this profile, in line * with their sensitive nature. - * @hide */ @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE) - @SystemApi public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE"; /** @@ -1785,7 +1780,11 @@ public class UserManager { /** * Specifies whether the user is allowed to modify default apps in settings. * - * <p>This restriction can be set by device or profile owner. + * <p>A device owner and a profile owner can set this restriction. When it is set by a + * device owner, it applies globally - i.e., modifying of default apps in Settings for all + * users is disallowed. When it is set by a profile owner on the primary user or by a profile + * owner of an organization-owned managed profile on the parent profile, modifying of + * default apps in Settings for the primary user is disallowed. * * <p>The default value is <code>false</code>. * @@ -3259,7 +3258,11 @@ public class UserManager { return isProfile(mUserId); } - private boolean isProfile(@UserIdInt int userId) { + /** + * Returns whether the specified user is a profile. + * @hide + */ + public boolean isProfile(@UserIdInt int userId) { final String profileType = getProfileType(userId); return profileType != null && !profileType.equals(""); } diff --git a/core/java/android/permission/PermissionGroupUsage.java b/core/java/android/permission/PermissionGroupUsage.java index 49b7463533e4..6895d3c67f06 100644 --- a/core/java/android/permission/PermissionGroupUsage.java +++ b/core/java/android/permission/PermissionGroupUsage.java @@ -17,6 +17,7 @@ package android.permission; import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -26,8 +27,8 @@ import com.android.internal.util.DataClass; /** * Represents the usage of a permission group by an app. Supports package name, user, permission - * group, whether or not the access is running or recent, whether the access is tied to a phone - * call, and an optional special attribution tag, label and proxy label. + * group, persistent device Id, whether or not the access is running or recent, whether the access + * is tied to a phone call, and an optional special attribution tag, label and proxy label. * * @hide */ @@ -48,6 +49,7 @@ public final class PermissionGroupUsage implements Parcelable { private final @Nullable CharSequence mAttributionTag; private final @Nullable CharSequence mAttributionLabel; private final @Nullable CharSequence mProxyLabel; + private final @NonNull String mPersistentDeviceId; @@ -79,7 +81,8 @@ public final class PermissionGroupUsage implements Parcelable { boolean phoneCall, @Nullable CharSequence attributionTag, @Nullable CharSequence attributionLabel, - @Nullable CharSequence proxyLabel) { + @Nullable CharSequence proxyLabel, + @NonNull String persistentDeviceId) { this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); @@ -93,6 +96,9 @@ public final class PermissionGroupUsage implements Parcelable { this.mAttributionTag = attributionTag; this.mAttributionLabel = attributionLabel; this.mProxyLabel = proxyLabel; + this.mPersistentDeviceId = persistentDeviceId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPersistentDeviceId); // onConstructed(); // You can define this method to get a callback } @@ -170,6 +176,12 @@ public final class PermissionGroupUsage implements Parcelable { return mProxyLabel; } + @DataClass.Generated.Member + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public @NonNull String getPersistentDeviceId() { + return mPersistentDeviceId; + } + @Override @DataClass.Generated.Member public String toString() { @@ -185,7 +197,8 @@ public final class PermissionGroupUsage implements Parcelable { "phoneCall = " + mPhoneCall + ", " + "attributionTag = " + mAttributionTag + ", " + "attributionLabel = " + mAttributionLabel + ", " + - "proxyLabel = " + mProxyLabel + + "proxyLabel = " + mProxyLabel + ", " + + "persistentDeviceId = " + mPersistentDeviceId + " }"; } @@ -210,7 +223,8 @@ public final class PermissionGroupUsage implements Parcelable { && mPhoneCall == that.mPhoneCall && java.util.Objects.equals(mAttributionTag, that.mAttributionTag) && java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel) - && java.util.Objects.equals(mProxyLabel, that.mProxyLabel); + && java.util.Objects.equals(mProxyLabel, that.mProxyLabel) + && java.util.Objects.equals(mPersistentDeviceId, that.mPersistentDeviceId); } @Override @@ -229,6 +243,7 @@ public final class PermissionGroupUsage implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel); _hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel); + _hash = 31 * _hash + java.util.Objects.hashCode(mPersistentDeviceId); return _hash; } @@ -252,6 +267,7 @@ public final class PermissionGroupUsage implements Parcelable { if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag); if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel); if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel); + dest.writeString(mPersistentDeviceId); } @Override @@ -275,6 +291,7 @@ public final class PermissionGroupUsage implements Parcelable { CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence(); CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence(); CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence(); + String persistentDeviceId = in.readString(); this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( @@ -289,6 +306,9 @@ public final class PermissionGroupUsage implements Parcelable { this.mAttributionTag = attributionTag; this.mAttributionLabel = attributionLabel; this.mProxyLabel = proxyLabel; + this.mPersistentDeviceId = persistentDeviceId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPersistentDeviceId); // onConstructed(); // You can define this method to get a callback } @@ -308,10 +328,10 @@ public final class PermissionGroupUsage implements Parcelable { }; @DataClass.Generated( - time = 1645067417023L, + time = 1706285211875L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final int mUid\nprivate final long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final boolean mActive\nprivate final boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final int mUid\nprivate final long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final boolean mActive\nprivate final boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nprivate final @android.annotation.NonNull java.lang.String mPersistentDeviceId\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index e6b8102764eb..fd52c769e408 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -1329,7 +1329,9 @@ public final class PermissionManager { public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) { // Lazily initialize the usage helper initializeUsageHelper(); - return mUsageHelper.getOpUsageData(micMuted); + boolean includeMicrophoneUsage = !micMuted; + return mUsageHelper.getOpUsageDataByDevice(includeMicrophoneUsage, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT); } /** diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 1f798baf1bd6..460b4dd9b86c 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -41,6 +41,8 @@ import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_AC import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.companion.virtual.VirtualDevice; +import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.Attribution; @@ -52,10 +54,12 @@ import android.location.LocationManager; import android.media.AudioManager; import android.os.Process; import android.os.UserHandle; +import android.permission.flags.Flags; import android.provider.DeviceConfig; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -75,6 +79,8 @@ import java.util.Objects; public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener, AppOpsManager.OnOpStartedListener { + private static final String LOG_TAG = PermissionUsageHelper.class.getName(); + /** * Whether to show the mic and camera icons. */ @@ -159,6 +165,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis private ArrayMap<UserHandle, Context> mUserContexts; private PackageManager mPkgManager; private AppOpsManager mAppOpsManager; + private VirtualDeviceManager mVirtualDeviceManager; @GuardedBy("mAttributionChains") private final ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains = new ArrayMap<>(); @@ -172,6 +179,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis mContext = context; mPkgManager = context.getPackageManager(); mAppOpsManager = context.getSystemService(AppOpsManager.class); + mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class); mUserContexts = new ArrayMap<>(); mUserContexts.put(Process.myUserHandle(), mContext); // TODO ntmyren: make this listen for flag enable/disable changes @@ -280,9 +288,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis } /** - * @see PermissionManager.getIndicatorAppOpUsageData + * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages for a device. + * The returned data is to power privacy indicator. */ - public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) { + public @NonNull List<PermissionGroupUsage> getOpUsageDataByDevice( + boolean includeMicrophoneUsage, String deviceId) { List<PermissionGroupUsage> usages = new ArrayList<>(); if (!shouldShowIndicators()) { @@ -293,11 +303,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis if (shouldShowLocationIndicator()) { ops.addAll(LOCATION_OPS); } - if (!isMicMuted) { + if (includeMicrophoneUsage) { ops.addAll(MIC_OPS); } - Map<String, List<OpUsage>> rawUsages = getOpUsages(ops); + Map<String, List<OpUsage>> rawUsages = getOpUsagesByDevice(ops, deviceId); ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); @@ -349,13 +359,40 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime, permGroup, usage.isRunning, isPhone, usage.attributionTag, attributionLabel, - usagesWithLabels.valueAt(usageNum))); + usagesWithLabels.valueAt(usageNum), + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)); } } return usages; } + /** + * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages and all connected + * devices. + * The returned data is to power privacy indicator. + */ + public @NonNull List<PermissionGroupUsage> getOpUsageDataForAllDevices( + boolean includeMicrophoneUsage) { + List<PermissionGroupUsage> allUsages = new ArrayList<>(); + List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices(); + ArraySet<String> persistentDeviceIds = new ArraySet<>(); + + for (int num = 0; num < virtualDevices.size(); num++) { + persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId()); + } + persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT); + + for (int index = 0; index < persistentDeviceIds.size(); index++) { + allUsages.addAll( + getOpUsageDataByDevice(includeMicrophoneUsage, + persistentDeviceIds.valueAt(index))); + } + + return allUsages; + } + + private void updateSubattributionLabelsMap(List<OpUsage> usages, ArrayMap<String, Map<String, String>> subAttributionLabelsMap) { if (usages == null || usages.isEmpty()) { @@ -443,12 +480,24 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis * running/recent info, if the usage is a phone call, per permission group. * * @param opNames a list of op names to get usage for + * @param deviceId which device to get op usage for * @return A map of permission group -> list of usages that are recent or running */ - private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) { + private Map<String, List<OpUsage>> getOpUsagesByDevice(List<String> opNames, String deviceId) { List<AppOpsManager.PackageOps> ops; try { - ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()])); + if (Flags.deviceAwarePermissionApisEnabled()) { + ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]), + deviceId); + } else if (!Objects.equals(deviceId, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) { + Slog.w(LOG_TAG, + "device_aware_permission_apis_enabled flag not enabled when deviceId is " + + "not default"); + return Collections.emptyMap(); + } else { + ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()])); + } } catch (NullPointerException e) { // older builds might not support all the app-ops requested return Collections.emptyMap(); diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 9d7fb7018d52..9218cb8f497d 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -96,9 +96,37 @@ flag { } flag { + name: "sensitive_notification_app_protection" + namespace: "permissions" + description: "This flag controls the sensitive notification app protections while screen sharing" + bug: "312784351" + # Referenced in WM where WM starts before DeviceConfig + is_fixed_read_only: true +} + +flag { name: "device_aware_permissions_enabled" is_fixed_read_only: true namespace: "permissions" description: "When the flag is off no permissions can be device aware" bug: "274852670" -}
\ No newline at end of file +} + +flag { + name: "get_emergency_role_holder_api_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Enables the getEmergencyRoleHolder API." + bug: "323157319" +} + +flag { + name: "new_permission_gid_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Enable new permission GID implementation" + bug: "325137277" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java index 786d768bc55b..aa47d3a5c2af 100644 --- a/core/java/android/service/notification/ZenPolicy.java +++ b/core/java/android/service/notification/ZenPolicy.java @@ -673,6 +673,10 @@ public final class ZenPolicy implements Parcelable { mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE; mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE; mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE; + + if (Flags.modesApi()) { + mZenPolicy.mAllowChannels = CHANNEL_POLICY_NONE; + } return this; } diff --git a/core/java/android/service/ondeviceintelligence/OWNERS b/core/java/android/service/ondeviceintelligence/OWNERS new file mode 100644 index 000000000000..09774f78d712 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/app/ondeviceintelligence/OWNERS diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 2028c4057c01..9db8aa167498 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -273,7 +273,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, - null); + false /* shiftDrawingOffsetForStartOverhang */, null); mEllipsizedWidth = outerwidth; mEllipsizedStart = 0; @@ -346,7 +346,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback ellipsizedWidth, ellipsize, 1 /* maxLines */, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, null /* rightIndents */, JUSTIFICATION_MODE_NONE, - LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, null); + LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, + false /* shiftDrawingOffsetForStartOverhang */, null); } /** @hide */ @@ -363,12 +364,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback TextUtils.TruncateAt ellipsize, Metrics metrics, boolean useBoundsForWidth, + boolean shiftDrawingOffsetForStartOverhang, @Nullable Paint.FontMetrics minimumFontMetrics) { this(text, paint, width, align, TextDirectionHeuristics.LTR, spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth, ellipsize, 1 /* maxLines */, Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE, null, null, Layout.JUSTIFICATION_MODE_NONE, - LineBreakConfig.NONE, metrics, useBoundsForWidth, minimumFontMetrics); + LineBreakConfig.NONE, metrics, useBoundsForWidth, + shiftDrawingOffsetForStartOverhang, minimumFontMetrics); } /* package */ BoringLayout( @@ -392,12 +395,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback LineBreakConfig lineBreakConfig, Metrics metrics, boolean useBoundsForWidth, + boolean shiftDrawingOffsetForStartOverhang, @Nullable Paint.FontMetrics minimumFontMetrics) { super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy, hyphenationFrequency, leftIndents, rightIndents, justificationMode, - lineBreakConfig, useBoundsForWidth, minimumFontMetrics); + lineBreakConfig, useBoundsForWidth, shiftDrawingOffsetForStartOverhang, + minimumFontMetrics); boolean trust; @@ -712,7 +717,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback int cursorOffset) { if (mDirect != null && highlight == null) { float leftShift = 0; - if (getUseBoundsForWidth()) { + if (getUseBoundsForWidth() && getShiftDrawingOffsetForStartOverhang()) { RectF drawingRect = computeDrawingBoundingBox(); if (drawingRect.left < 0) { leftShift = -drawingRect.left; diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 928604983b70..cce4f7bf7b1f 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -25,6 +25,7 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Paint; import android.graphics.Rect; @@ -317,6 +318,35 @@ public class DynamicLayout extends Layout { } /** + * Set true for shifting the drawing x offset for showing overhang at the start position. + * + * This flag is ignored if the {@link #getUseBoundsForWidth()} is false. + * + * If this value is false, the Layout draws text from the zero even if there is a glyph + * stroke in a region where the x coordinate is negative. + * + * If this value is true, the Layout draws text with shifting the x coordinate of the + * drawing bounding box. + * + * This value is false by default. + * + * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for + * showing the stroke that is in the region where + * the x coordinate is negative. + * @see #setUseBoundsForWidth(boolean) + * @see #getUseBoundsForWidth() + */ + @NonNull + // The corresponding getter is getShiftDrawingOffsetForStartOverhang() + @SuppressLint("MissingGetterMatchingBuilder") + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public Builder setShiftDrawingOffsetForStartOverhang( + boolean shiftDrawingOffsetForStartOverhang) { + mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang; + return this; + } + + /** * Set the minimum font metrics used for line spacing. * * <p> @@ -386,6 +416,7 @@ public class DynamicLayout extends Layout { private int mEllipsizedWidth; private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; private @Nullable Paint.FontMetrics mMinimumFontMetrics; private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -462,7 +493,8 @@ public class DynamicLayout extends Layout { false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize, Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency, null /* leftIndents */, null /* rightIndents */, justificationMode, - lineBreakConfig, false /* useBoundsForWidth */, null /* minimumFontMetrics */); + lineBreakConfig, false /* useBoundsForWidth */, false, + null /* minimumFontMetrics */); final Builder b = Builder.obtain(base, paint, width) .setAlignment(align) @@ -488,7 +520,8 @@ public class DynamicLayout extends Layout { b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency, null /* leftIndents */, null /* rightIndents */, b.mJustificationMode, - b.mLineBreakConfig, b.mUseBoundsForWidth, b.mMinimumFontMetrics); + b.mLineBreakConfig, b.mUseBoundsForWidth, b.mShiftDrawingOffsetForStartOverhang, + b.mMinimumFontMetrics); mDisplay = b.mDisplay; mIncludePad = b.mIncludePad; @@ -516,6 +549,7 @@ public class DynamicLayout extends Layout { mBase = b.mBase; mFallbackLineSpacing = b.mFallbackLineSpacing; mUseBoundsForWidth = b.mUseBoundsForWidth; + mShiftDrawingOffsetForStartOverhang = b.mShiftDrawingOffsetForStartOverhang; mMinimumFontMetrics = b.mMinimumFontMetrics; if (b.mEllipsize != null) { mInts = new PackedIntVector(COLUMNS_ELLIPSIZE); @@ -713,6 +747,7 @@ public class DynamicLayout extends Layout { .setAddLastLineLineSpacing(!islast) .setIncludePad(false) .setUseBoundsForWidth(mUseBoundsForWidth) + .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang) .setMinimumFontMetrics(mMinimumFontMetrics) .setCalculateBounds(true); @@ -1392,6 +1427,7 @@ public class DynamicLayout extends Layout { private Rect mTempRect = new Rect(); private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; @Nullable Paint.FontMetrics mMinimumFontMetrics; @UnsupportedAppUsage diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index e5d199ad8e46..8e52af3fb57b 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -44,6 +44,7 @@ import android.text.style.LineBackgroundSpan; import android.text.style.ParagraphStyle; import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; +import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -299,7 +300,7 @@ public abstract class Layout { this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null, - JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, null); + JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, false, null); } /** @@ -349,6 +350,7 @@ public abstract class Layout { int justificationMode, LineBreakConfig lineBreakConfig, boolean useBoundsForWidth, + boolean shiftDrawingOffsetForStartOverhang, Paint.FontMetrics minimumFontMetrics ) { @@ -384,6 +386,7 @@ public abstract class Layout { mJustificationMode = justificationMode; mLineBreakConfig = lineBreakConfig; mUseBoundsForWidth = useBoundsForWidth; + mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang; mMinimumFontMetrics = minimumFontMetrics; } @@ -465,7 +468,7 @@ public abstract class Layout { @Nullable Paint selectionPaint, int cursorOffsetVertical) { float leftShift = 0; - if (mUseBoundsForWidth) { + if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) { RectF drawingRect = computeDrawingBoundingBox(); if (drawingRect.left < 0) { leftShift = -drawingRect.left; @@ -3414,6 +3417,7 @@ public abstract class Layout { private int mJustificationMode; private LineBreakConfig mLineBreakConfig; private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; private @Nullable Paint.FontMetrics mMinimumFontMetrics; private TextLine.LineInfo mLineInfo = null; @@ -3873,6 +3877,35 @@ public abstract class Layout { } /** + * Set true for shifting the drawing x offset for showing overhang at the start position. + * + * This flag is ignored if the {@link #getUseBoundsForWidth()} is false. + * + * If this value is false, the Layout draws text from the zero even if there is a glyph + * stroke in a region where the x coordinate is negative. + * + * If this value is true, the Layout draws text with shifting the x coordinate of the + * drawing bounding box. + * + * This value is false by default. + * + * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for + * showing the stroke that is in the region where + * the x coordinate is negative. + * @see #setUseBoundsForWidth(boolean) + * @see #getUseBoundsForWidth() + */ + @NonNull + // The corresponding getter is getShiftDrawingOffsetForStartOverhang() + @SuppressLint("MissingGetterMatchingBuilder") + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public Builder setShiftDrawingOffsetForStartOverhang( + boolean shiftDrawingOffsetForStartOverhang) { + mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang; + return this; + } + + /** * Set the minimum font metrics used for line spacing. * * <p> @@ -3948,6 +3981,7 @@ public abstract class Layout { .setJustificationMode(mJustificationMode) .setLineBreakConfig(mLineBreakConfig) .setUseBoundsForWidth(mUseBoundsForWidth) + .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang) .build(); } else { return new BoringLayout( @@ -3955,7 +3989,7 @@ public abstract class Layout { mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines, mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents, mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth, - mMinimumFontMetrics); + mShiftDrawingOffsetForStartOverhang, mMinimumFontMetrics); } } @@ -3980,6 +4014,7 @@ public abstract class Layout { private int mJustificationMode = JUSTIFICATION_MODE_NONE; private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; private Paint.FontMetrics mMinimumFontMetrics; } @@ -4294,6 +4329,20 @@ public abstract class Layout { } /** + * Returns true if shifting drawing offset for start overhang. + * + * @return True if shifting drawing offset for start overhang. + * @see android.widget.TextView#setShiftDrawingOffsetForStartOverhang(boolean) + * @see TextView#getShiftDrawingOffsetForStartOverhang() + * @see StaticLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean) + * @see DynamicLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean) + */ + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public boolean getShiftDrawingOffsetForStartOverhang() { + return mShiftDrawingOffsetForStartOverhang; + } + + /** * Get the minimum font metrics used for line spacing. * * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics) diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 5986238d3035..3dd3a9ea8baf 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -24,6 +24,7 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Paint; import android.graphics.RectF; @@ -454,6 +455,35 @@ public class StaticLayout extends Layout { } /** + * Set true for shifting the drawing x offset for showing overhang at the start position. + * + * This flag is ignored if the {@link #getUseBoundsForWidth()} is false. + * + * If this value is false, the Layout draws text from the zero even if there is a glyph + * stroke in a region where the x coordinate is negative. + * + * If this value is true, the Layout draws text with shifting the x coordinate of the + * drawing bounding box. + * + * This value is false by default. + * + * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for + * showing the stroke that is in the region where + * the x coordinate is negative. + * @see #setUseBoundsForWidth(boolean) + * @see #getUseBoundsForWidth() + */ + @NonNull + // The corresponding getter is getShiftDrawingOffsetForStartOverhang() + @SuppressLint("MissingGetterMatchingBuilder") + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public Builder setShiftDrawingOffsetForStartOverhang( + boolean shiftDrawingOffsetForStartOverhang) { + mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang; + return this; + } + + /** * Internal API that tells underlying line breaker that calculating bounding boxes even if * the line break is performed with advances. This is useful for DynamicLayout internal * implementation because it uses bounding box as well as advances. @@ -566,6 +596,7 @@ public class StaticLayout extends Layout { private boolean mAddLastLineLineSpacing; private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; private boolean mCalculateBounds; @Nullable private Paint.FontMetrics mMinimumFontMetrics; @@ -599,6 +630,7 @@ public class StaticLayout extends Layout { JUSTIFICATION_MODE_NONE, null, // lineBreakConfig, false, // useBoundsForWidth + false, // shiftDrawingOffsetForStartOverhang null // minimumFontMetrics ); @@ -677,7 +709,7 @@ public class StaticLayout extends Layout { b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents, b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth, - b.mMinimumFontMetrics); + b.mShiftDrawingOffsetForStartOverhang, b.mMinimumFontMetrics); mColumns = columnSize; if (b.mEllipsize != null) { diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index d2c5975ea356..0a73fd1689c3 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -50,7 +50,7 @@ import java.util.regex.Pattern; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.EventLog_host") + "com.android.platform.test.ravenwood.nativesubstitution.EventLog_host") public class EventLog { /** @hide */ public EventLog() {} diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 31576c5c74fc..b33214dffb7e 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -73,7 +73,7 @@ import java.net.UnknownHostException; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.Log_host") + "com.android.platform.test.ravenwood.nativesubstitution.Log_host") public final class Log { /** @hide */ @IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE}) diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index ffe0c716905d..5ec415910d63 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -19,10 +19,11 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; +import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.hardware.HardwareBuffer; -import android.os.IBinder; +import android.os.Looper; import android.window.InputTransferToken; import android.window.SurfaceSyncGroup; @@ -180,66 +181,25 @@ public interface AttachedSurfaceControl { } /** - * Gets the token used for associating this {@link AttachedSurfaceControl} with - * {@link SurfaceControlViewHost} instances. - * - * <p>This token should be passed to {@link SurfaceControlViewHost}'s constructor. - * This token will be {@code null} if the window does not have an input channel. - * - * @return The SurfaceControlViewHost link token. - */ - @Nullable - @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API) - default IBinder getHostToken() { - throw new UnsupportedOperationException("The getHostToken needs to be " - + "implemented before making this call."); - } - - /** * Gets the token used for associating this {@link AttachedSurfaceControl} with an embedded * {@link SurfaceControlViewHost} or {@link SurfaceControl} * - * @return The SurfaceControlViewHost link token. This can return {@code null} if the - * {@link AttachedSurfaceControl} was created with no registered input - * @hide + * <p>This token should be passed to + * {@link SurfaceControlViewHost#SurfaceControlViewHost(Context, Display, InputTransferToken)} + * or + * {@link WindowManager#registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, + * SurfaceControl, Choreographer, SurfaceControlInputReceiver)} or + * {@link WindowManager#registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, + * SurfaceControl, Looper, SurfaceControlInputReceiver)} + * + * @return The SurfaceControlViewHost link token. + * @throws IllegalStateException if the {@link AttachedSurfaceControl} was created with no + * registered input */ - @Nullable + @NonNull + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) default InputTransferToken getInputTransferToken() { - throw new UnsupportedOperationException("The getHostToken needs to be " + throw new UnsupportedOperationException("The getInputTransferToken needs to be " + "implemented before making this call."); } - - /** - * Transfer the currently in progress touch gesture from the host to the requested - * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the - * SurfaceControlViewHost was created with the current host's inputToken. - * <p> - * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL - * and does not receive any further input events for this gesture. - * <p> - * The transferred-to window receives an ACTION_DOWN event and then the remainder of the - * input events for this gesture. It does not receive any of the previous events of this gesture - * that the originating window received. - * <p> - * The "transferTouch" API only works for the current gesture. When a new gesture arrives, - * input dispatcher will do a new round of hit testing. So, if the "host" window is still the - * first thing that's being touched, then it will receive the new gesture again. It will - * again be up to the host to transfer this new gesture to the embedded. - * <p> - * Once the transferred-to window receives the gesture, it can choose to give up this gesture - * and send it to another window that it's linked to (it can't be an arbitrary window for - * security reasons) using the same transferTouch API. Only the window currently receiving - * touch is allowed to transfer the gesture. - * - * @param surfacePackage The SurfacePackage to transfer the gesture to. - * @return Whether the touch stream was transferred. - * @see SurfaceControlViewHost#transferTouchGestureToHost() for the reverse to transfer touch - * gesture from the embedded to the host. - */ - @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED) - default boolean transferHostTouchGestureToEmbedded( - @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) { - throw new UnsupportedOperationException( - "transferHostTouchGestureToEmbedded is unimplemented"); - } } diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index eb289204e481..676b903472c5 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -23,6 +23,9 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.text.TextUtils; +import android.view.inputmethod.ConnectionlessHandwritingCallback; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.Flags; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; @@ -225,15 +228,7 @@ public class HandwritingInitiator { } startHandwriting(candidateView); } else if (candidateView.getHandwritingDelegatorCallback() != null) { - String delegatePackageName = - candidateView.getAllowedHandwritingDelegatePackageName(); - if (delegatePackageName == null) { - delegatePackageName = candidateView.getContext().getOpPackageName(); - } - mImm.prepareStylusHandwritingDelegation( - candidateView, delegatePackageName); - candidateView.getHandwritingDelegatorCallback().run(); - mState.mHasPreparedHandwritingDelegation = true; + prepareDelegation(candidateView); } else { if (!mInitiateWithoutConnection) { mState.mPendingConnectedView = new WeakReference<>(candidateView); @@ -375,7 +370,7 @@ public class HandwritingInitiator { // A new view just gain focus. By default, we should show hover icon for it. mShowHoverIconForConnectedView = true; } - if (!fromTouchEvent) { + if (!fromTouchEvent && view.isHandwritingDelegate()) { tryAcceptStylusHandwritingDelegation(view); } return true; @@ -393,15 +388,33 @@ public class HandwritingInitiator { } } + private void prepareDelegation(View view) { + String delegatePackageName = view.getAllowedHandwritingDelegatePackageName(); + if (delegatePackageName == null) { + delegatePackageName = view.getContext().getOpPackageName(); + } + if (mImm.isConnectionlessStylusHandwritingAvailable()) { + // No other view should have focus during the connectionless handwriting session, as + // this could cause user confusion about the input target for the session. + view.getViewRootImpl().getView().clearFocus(); + mImm.startConnectionlessStylusHandwritingForDelegation( + view, getCursorAnchorInfoForConnectionless(view), delegatePackageName, + view::post, new DelegationCallback(view, delegatePackageName)); + mState.mHasInitiatedHandwriting = true; + mState.mShouldInitHandwriting = false; + } else { + mImm.prepareStylusHandwritingDelegation(view, delegatePackageName); + view.getHandwritingDelegatorCallback().run(); + mState.mHasPreparedHandwritingDelegation = true; + } + } + /** * Starts a stylus handwriting session for the delegate view, if {@link * InputMethodManager#prepareStylusHandwritingDelegation} was previously called. */ @VisibleForTesting public boolean tryAcceptStylusHandwritingDelegation(@NonNull View view) { - if (!view.isHandwritingDelegate() || (mState != null && mState.mHasInitiatedHandwriting)) { - return false; - } String delegatorPackageName = view.getAllowedHandwritingDelegatorPackageName(); if (delegatorPackageName == null) { @@ -807,6 +820,59 @@ public class HandwritingInitiator { && view.shouldInitiateHandwriting(); } + private CursorAnchorInfo getCursorAnchorInfoForConnectionless(View view) { + CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder(); + // Fake editor views will usually display hint text. The hint text view can be used to + // populate the CursorAnchorInfo. + TextView textView = findFirstTextViewDescendent(view); + if (textView != null) { + textView.getCursorAnchorInfo(0, builder, mTempMatrix); + if (textView.getSelectionStart() < 0) { + // Insertion marker location is not populated if selection start is negative, so + // make a best guess. + float bottom = textView.getHeight() - textView.getExtendedPaddingBottom(); + builder.setInsertionMarkerLocation( + /* horizontalPosition= */ textView.getCompoundPaddingStart(), + /* lineTop= */ textView.getExtendedPaddingTop(), + /* lineBaseline= */ bottom, + /* lineBottom= */ bottom, + /* flags= */ 0); + } + } else { + // If there is no TextView descendent, just populate the insertion marker with the start + // edge of the view. + mTempMatrix.reset(); + view.transformMatrixToGlobal(mTempMatrix); + builder.setMatrix(mTempMatrix); + builder.setInsertionMarkerLocation( + /* horizontalPosition= */ view.isLayoutRtl() ? view.getWidth() : 0, + /* lineTop= */ 0, + /* lineBaseline= */ view.getHeight(), + /* lineBottom= */ view.getHeight(), + /* flags= */ 0); + } + return builder.build(); + } + + @Nullable + private static TextView findFirstTextViewDescendent(View view) { + if (view instanceof ViewGroup viewGroup) { + TextView textView; + for (int i = 0; i < viewGroup.getChildCount(); ++i) { + View child = viewGroup.getChildAt(i); + textView = (child instanceof TextView tv) + ? tv : findFirstTextViewDescendent(viewGroup.getChildAt(i)); + if (textView != null + && textView.isAggregatedVisible() + && (!TextUtils.isEmpty(textView.getText()) + || !TextUtils.isEmpty(textView.getHint()))) { + return textView; + } + } + } + return null; + } + /** * A class used to track the handwriting areas set by the Views. * @@ -931,4 +997,35 @@ public class HandwritingInitiator { return true; } } + + private class DelegationCallback implements ConnectionlessHandwritingCallback { + private final View mView; + private final String mDelegatePackageName; + + private DelegationCallback(View view, String delegatePackageName) { + mView = view; + mDelegatePackageName = delegatePackageName; + } + + @Override + public void onResult(@NonNull CharSequence text) { + mView.getHandwritingDelegatorCallback().run(); + } + + @Override + public void onError(int errorCode) { + switch (errorCode) { + case CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED: + mView.getHandwritingDelegatorCallback().run(); + break; + case CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED: + // Fall back to the old delegation flow + mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName); + mView.getHandwritingDelegatorCallback().run(); + mState.mHasInitiatedHandwriting = false; + mState.mHasPreparedHandwritingDelegation = true; + break; + } + } + } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 29cc8594deec..c475f6babbf1 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -1098,4 +1098,7 @@ interface IWindowManager * (ie. not handled by any window which can handle the drag). */ void setUnhandledDragListener(IUnhandledDragListener listener); + + boolean transferTouchGesture(in InputTransferToken transferFromToken, + in InputTransferToken transferToToken); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 55e49f8f3ad9..d68a47c54d4b 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -370,11 +370,6 @@ interface IWindowSession { */ boolean cancelDraw(IWindow window); - boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow); - - boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, - in InputTransferToken transferTouchToken); - /** * Moves the focus to the adjacent window if there is one in the given direction. This can only * move the focus to the window in the same leaf task. diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 58765b46a05a..06a923afffbb 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -33,6 +34,8 @@ import android.window.ISurfaceSyncGroup; import android.window.InputTransferToken; import android.window.WindowTokenClient; +import com.android.window.flags.Flags; + import dalvik.system.CloseGuard; import java.util.Objects; @@ -290,12 +293,12 @@ public class SurfaceControlViewHost { /** * Gets an {@link InputTransferToken} which can be used to request focus on the embedded * surface or to transfer touch gesture to the embedded surface. - * @return the InputTransferToken associated with {@link SurfacePackage} - * @see AttachedSurfaceControl#transferHostTouchGestureToEmbedded(SurfacePackage) - * - * @hide + * @return the InputTransferToken associated with {@link SurfacePackage} or {@code null} if + * the embedded hasn't set up its view or doesn't have input. + * @see WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken) */ @Nullable + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) public InputTransferToken getInputTransferToken() { return mInputTransferToken; } @@ -347,6 +350,25 @@ public class SurfaceControlViewHost { @Nullable IBinder hostToken) { this(context, display, hostToken == null ? null : new InputTransferToken(hostToken), "untracked"); + + } + + /** + * Construct a new SurfaceControlViewHost. The root Surface will be + * allocated internally and is accessible via getSurfacePackage(). + * <p> + * The hostInputTransferToken parameter allows the host and embedded to be associated with + * each other to allow transferring touch gesture and focus. This is also used for ANR + * reporting. It's accessible from {@link AttachedSurfaceControl#getInputTransferToken()}. + * + * @param context The Context object for your activity or application. + * @param display The Display the hierarchy will be placed on. + * @param hostInputTransferToken The host input transfer token, as discussed above. + */ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, + @Nullable InputTransferToken hostInputTransferToken) { + this(context, display, hostInputTransferToken, "untracked"); } /** @@ -555,9 +577,9 @@ public class SurfaceControlViewHost { } /** - * Transfer the currently in progress touch gesture to the parent - * (if any) of this SurfaceControlViewHost. This requires that the - * SurfaceControlViewHost was created with an associated hostInputToken. + * Transfer the currently in progress touch gesture to the parent (if any) of this + * SurfaceControlViewHost. This requires that the SurfaceControlViewHost was created with an + * associated host {@link InputTransferToken}. * * @return Whether the touch stream was transferred. */ @@ -565,13 +587,14 @@ public class SurfaceControlViewHost { if (mViewRoot == null) { return false; } - - final IWindowSession realWm = WindowManagerGlobal.getWindowSession(); - try { - return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); + final WindowManager wm = + (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE); + InputTransferToken embeddedToken = getInputTransferToken(); + InputTransferToken hostToken = mWm.mHostInputTransferToken; + if (embeddedToken == null || hostToken == null) { + Log.w(TAG, "Failed to transferTouchGestureToHost. Host or embedded token is null"); + return false; } - return false; + return wm.transferTouchGesture(getInputTransferToken(), mWm.mHostInputTransferToken); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 254c4aed57c1..73b6ed650394 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -18,6 +18,7 @@ package android.view; import static android.content.res.Resources.ID_NULL; import static android.os.Trace.TRACE_TAG_APP; +import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION; import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; import static android.view.Surface.FRAME_RATE_CATEGORY_LOW; @@ -5637,7 +5638,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) - public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0; + public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN; @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1; @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) @@ -6995,7 +6996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setCredentialManagerRequest */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public void clearCredentialManagerRequest() { if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) { Log.v(AUTOFILL_LOG_TAG, "clearCredentialManagerRequest called"); @@ -7027,7 +7028,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param callback to be invoked when either a response or an exception needs to be * propagated for the given view */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public void setCredentialManagerRequest(@NonNull GetCredentialRequest request, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) { Preconditions.checkNotNull(request, "request must not be null"); @@ -9944,7 +9945,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The credential request associated with this View. */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) @Nullable public final GetCredentialRequest getCredentialManagerRequest() { if (mViewCredentialHandler == null) { @@ -9968,7 +9969,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The callback associated with this view that will be invoked on a response from * {@link CredentialManager} . */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) @Nullable public final OutcomeReceiver<GetCredentialResponse, GetCredentialException> getCredentialManagerCallback() { @@ -33456,7 +33457,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return FRAME_RATE_CATEGORY_NORMAL; } - return mLastFrameRateCategory; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 28a73344b731..75deceb5826f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -27,6 +27,8 @@ import static android.view.InputDevice.SOURCE_CLASS_NONE; import static android.view.InsetsSource.ID_IME; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT; +import static android.view.Surface.FRAME_RATE_CATEGORY_LOW; +import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; @@ -103,7 +105,6 @@ import android.accessibilityservice.AccessibilityService; import android.animation.AnimationHandler; import android.animation.LayoutTransition; import android.annotation.AnyThread; -import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; @@ -240,7 +241,6 @@ import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; import com.android.internal.view.SurfaceCallbackHelper; import com.android.modules.expresslog.Counter; -import com.android.window.flags.Flags; import java.io.IOException; import java.io.OutputStream; @@ -1022,6 +1022,11 @@ public final class ViewRootImpl implements ViewParent, // Used to check if there is a message in the message queue // for idleness handling. private boolean mHasIdledMessage = false; + // Used to allow developers to opt out Toolkit dVRR feature. + // This feature allows device to adjust refresh rate + // as needed and can be useful for power saving. + // Should not enable the dVRR feature if the value is false. + private boolean mIsFrameRatePowerSavingsBalanced = true; // time for touch boost period. private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000; // time for checking idle status periodically. @@ -1033,6 +1038,15 @@ public final class ViewRootImpl implements ViewParent, private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100; /* + * The variables below are used to update frame rate category + */ + private static final int FRAME_RATE_CATEGORY_COUNT = 5; + private int mFrameRateCategoryHighCount = 0; + private int mFrameRateCategoryHighHintCount = 0; + private int mFrameRateCategoryNormalCount = 0; + private int mFrameRateCategoryLowCount = 0; + + /* * the variables below are used to determine whther a dVRR feature should be enabled */ @@ -2524,8 +2538,10 @@ public final class ViewRootImpl implements ViewParent, // Set the frame rate selection strategy to FRAME_RATE_SELECTION_STRATEGY_SELF // This strategy ensures that the frame rate specifications do not cascade down to // the descendant layers. This is particularly important for applications like Chrome, - // where child surfaces should adhere to default behavior instead of no preference - if (sToolkitSetFrameRateReadOnlyFlagValue) { + // where child surfaces should adhere to default behavior instead of no preference. + // This issue only happens when ViewRootImpl calls setFrameRateCategory. This is + // no longer needed if the dVRR feature is disabled. + if (shouldEnableDvrr()) { try { mFrameRateTransaction.setFrameRateSelectionStrategy(sc, sc.FRAME_RATE_SELECTION_STRATEGY_SELF).applyAsyncUnsafe(); @@ -3249,7 +3265,7 @@ public final class ViewRootImpl implements ViewParent, destroyHardwareResources(); } - if (sToolkitSetFrameRateReadOnlyFlagValue && viewVisibility == View.VISIBLE) { + if (shouldEnableDvrr() && viewVisibility == View.VISIBLE) { // Boost frame rate when the viewVisibility becomes true. // This is mainly for lanuchers that lanuch new windows. boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); @@ -3964,7 +3980,7 @@ public final class ViewRootImpl implements ViewParent, } } - if (sToolkitSetFrameRateReadOnlyFlagValue) { + if (shouldEnableDvrr()) { // Boost the frame rate when the ViewRootImpl first becomes available. boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); } @@ -4086,7 +4102,14 @@ public final class ViewRootImpl implements ViewParent, // when the values are applicable. setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); + mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0 + ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount; + mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0 + ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount; + mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0 + ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; + mPreferredFrameRate = -1; } private void createSyncIfNeeded() { @@ -5171,7 +5194,10 @@ public final class ViewRootImpl implements ViewParent, // Force recalculation of transparent regions if (accessibilityFocusDirty) { - requestLayout(); + final Rect bounds = mAttachInfo.mTmpInvalRect; + if (getAccessibilityFocusedRect(bounds)) { + requestLayout(); + } } mAttachInfo.mDrawingTime = @@ -11220,25 +11246,15 @@ public final class ViewRootImpl implements ViewParent, } /** - * @return Returns a token used for associating the root surface - * to {@link SurfaceControlViewHost}. - */ - @Nullable - @Override - @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API) - public IBinder getHostToken() { - return getInputToken(); - } - - /** * {@inheritDoc} */ - @Nullable + @NonNull @Override public InputTransferToken getInputTransferToken() { IBinder inputToken = getInputToken(); if (inputToken == null) { - return null; + throw new IllegalStateException( + "Called getInputTransferToken for Window with no input channel"); } return new InputTransferToken(inputToken); } @@ -12308,7 +12324,8 @@ public final class ViewRootImpl implements ViewParent, } try { - if (mLastPreferredFrameRate != preferredFrameRate) { + if (mLastPreferredFrameRate != preferredFrameRate + && preferredFrameRate >= 0) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate " @@ -12333,12 +12350,12 @@ public final class ViewRootImpl implements ViewParent, private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change - return mSurface.isValid() && sToolkitSetFrameRateReadOnlyFlagValue; + return mSurface.isValid() && shouldEnableDvrr(); } private boolean shouldSetFrameRate() { // use toolkitSetFrameRate flag to gate the change - return sToolkitSetFrameRateReadOnlyFlagValue; + return mSurface.isValid() && mPreferredFrameRate > 0 && shouldEnableDvrr(); } private boolean shouldTouchBoost(int motionEventAction, int windowType) { @@ -12347,7 +12364,7 @@ public final class ViewRootImpl implements ViewParent, || motionEventAction == MotionEvent.ACTION_UP; boolean undesiredType = windowType == TYPE_INPUT_METHOD && mShouldSuppressBoostOnTyping; // use toolkitSetFrameRate flag to gate the change - return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue + return desiredAction && !undesiredType && shouldEnableDvrr() && getFrameRateBoostOnTouchEnabled(); } @@ -12358,7 +12375,25 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) public void votePreferredFrameRateCategory(int frameRateCategory) { - mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory); + if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH) { + mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; + } else if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) { + mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT; + } else if (frameRateCategory == FRAME_RATE_CATEGORY_NORMAL) { + mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; + } else if (frameRateCategory == FRAME_RATE_CATEGORY_LOW) { + mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT; + } + + if (mFrameRateCategoryHighCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; + } else if (mFrameRateCategoryHighHintCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; + } else if (mFrameRateCategoryNormalCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; + } else if (mFrameRateCategoryLowCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; + } mHasInvalidation = true; } @@ -12380,13 +12415,7 @@ public final class ViewRootImpl implements ViewParent, return; } - if (mPreferredFrameRate == 0) { - mPreferredFrameRate = frameRate; - } else if (frameRate > 60 || mPreferredFrameRate > 60) { - mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate); - } else if (mPreferredFrameRate != frameRate) { - mPreferredFrameRate = 60; - } + mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate); mHasInvalidation = true; mHandler.removeMessages(MSG_FRAME_RATE_SETTING); @@ -12415,7 +12444,7 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting public float getPreferredFrameRate() { - return mPreferredFrameRate; + return mPreferredFrameRate >= 0 ? mPreferredFrameRate : mLastPreferredFrameRate; } /** @@ -12443,19 +12472,6 @@ public final class ViewRootImpl implements ViewParent, boostTimeOut); } - @Override - public boolean transferHostTouchGestureToEmbedded( - @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) { - final IWindowSession realWm = WindowManagerGlobal.getWindowSession(); - try { - return realWm.transferHostTouchGestureToEmbedded(mWindow, - surfacePackage.getInputTransferToken()); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } - return false; - } - /** * Set the default back key callback for windowless window, to forward the back key event * to host app. @@ -12470,4 +12486,27 @@ public final class ViewRootImpl implements ViewParent, // Record the largest view of percentage to the display size. mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage); } + + /** + * Get the value of mIsFrameRatePowerSavingsBalanced + * Can be used to checked if toolkit dVRR feature is enabled. The default value is true. + */ + @VisibleForTesting + public boolean isFrameRatePowerSavingsBalanced() { + return mIsFrameRatePowerSavingsBalanced; + } + + /** + * Set the value of mIsFrameRatePowerSavingsBalanced + * Can be used to checked if toolkit dVRR feature is enabled. + */ + public void setFrameRatePowerSavingsBalanced(boolean enabled) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + mIsFrameRatePowerSavingsBalanced = enabled; + } + } + + private boolean shouldEnableDvrr() { + return sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced; + } } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index d86cc4ac781d..131fca7d923a 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -16,6 +16,8 @@ package android.view; +import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION; + import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -361,7 +363,7 @@ public abstract class ViewStructure { * {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)} */ @Nullable - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public GetCredentialRequest getCredentialManagerRequest() { return null; } @@ -376,7 +378,7 @@ public abstract class ViewStructure { * {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)} */ @Nullable - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public OutcomeReceiver< GetCredentialResponse, GetCredentialException> getCredentialManagerCallback() { return null; @@ -551,7 +553,7 @@ public abstract class ViewStructure { * @param request the request to be fired * @param callback the callback where the response or exception, is returned */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public void setCredentialManagerRequest(@NonNull GetCredentialRequest request, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {} @@ -559,7 +561,7 @@ public abstract class ViewStructure { * Clears the credential request previously set through * {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)} */ - @FlaggedApi("autofill_credman_dev_integration") + @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) public void clearCredentialManagerRequest() {} /** diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 4ba4ee3ffff7..6b427fc00766 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1409,6 +1409,42 @@ public abstract class Window { } /** + * Set whether frameratepowersavingsbalance is enabled for this Window. + * This allows device to adjust refresh rate + * as needed and can be useful for power saving. + * + * @param enabled whether the frameratepowersavingsbalance is enabled. + * @see #isFrameRatePowerSavingsBalanced() + * @see WindowManager.LayoutParams#setFrameRatePowerSavingsBalanced(boolean) + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void setFrameRatePowerSavingsBalanced(boolean enabled) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.setFrameRatePowerSavingsBalanced(enabled); + dispatchWindowAttributesChanged(attrs); + } + } + + /** + * Get whether frameratepowersavingsbalance is enabled for this Window. + * This allows device to adjust refresh rate + * as needed and can be useful for power saving. + * {@link #setFrameRateBoostOnTouchEnabled(boolean)} + * + * @return whether the frameratepowersavingsbalance is enabled. + * @see #setFrameRatePowerSavingsBalanced(boolean) + * @see WindowManager.LayoutParams#isFrameRatePowerSavingsBalanced() + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public boolean isFrameRatePowerSavingsBalanced() { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + return getAttributes().isFrameRatePowerSavingsBalanced(); + } + return false; + } + + /** * If {@code isPreferred} is true, this method requests that the connected display does minimal * post processing when this window is visible on the screen. Otherwise, it requests that the * display switches back to standard image processing. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 58fb273212f7..0e17626c5598 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -123,6 +123,7 @@ import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityNodeInfo; +import android.window.InputTransferToken; import android.window.TaskFpsCallback; import android.window.TrustedPresentationThresholds; @@ -1547,6 +1548,48 @@ public interface WindowManager extends ViewManager { "android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"; /** + * Application or Activity level + * {@link android.content.pm.PackageManager.Property PackageManager.Property} to provide any + * preferences for showing all or specific Activities on small cover displays of foldable + * style devices. + * + * <p>The only supported value for the property is {@link #COMPAT_SMALL_COVER_SCREEN_OPT_IN}. + * + * <p><b>Syntax:</b> + * <pre> + * <application> + * <property + * android:name="android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN" + * android:value=1 <!-- COMPAT_COVER_SCREEN_OPT_IN -->/> + * </application> + * </pre> + */ + @FlaggedApi(Flags.FLAG_COVER_DISPLAY_OPT_IN) + String PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN = + "android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN"; + + /** + * Value applicable for the {@link #PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN} property to + * provide a signal to the system that an application or its specific activities explicitly + * opt into being displayed on small foldable device cover screens that measure at least 1.5 + * inches for the shorter dimension and at least 2.4 inches for the longer dimension. + */ + @CompatSmallScreenPolicy + @FlaggedApi(Flags.FLAG_COVER_DISPLAY_OPT_IN) + int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1; + + /** + * @hide + */ + @IntDef({ + COMPAT_SMALL_COVER_SCREEN_OPT_IN, + }) + @Retention(RetentionPolicy.SOURCE) + @interface CompatSmallScreenPolicy {} + + + + /** * Request for app's keyboard shortcuts to be retrieved asynchronously. * * @param receiver The callback to be triggered when the result is ready. @@ -4396,6 +4439,7 @@ public interface WindowManager extends ViewManager { * For variable refresh rate project. */ private boolean mFrameRateBoostOnTouch = true; + private boolean mIsFrameRatePowerSavingsBalanced = true; private static boolean sToolkitSetFrameRateReadOnlyFlagValue = android.view.flags.Flags.toolkitSetFrameRateReadOnly(); @@ -4860,6 +4904,36 @@ public interface WindowManager extends ViewManager { } /** + * Set the value whether frameratepowersavingsbalance is enabled for this Window. + * This allows device to adjust refresh rate + * as needed and can be useful for power saving. + * + * @param enabled Whether we should enable frameratepowersavingsbalance. + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void setFrameRatePowerSavingsBalanced(boolean enabled) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + mIsFrameRatePowerSavingsBalanced = enabled; + } + } + + /** + * Get the value whether frameratepowersavingsbalance is enabled for this Window. + * This allows device to adjust refresh rate + * as needed and can be useful for power saving. + * by {@link #setFrameRatePowerSavingsBalanced(boolean)} + * + * @return Whether we should enable frameratepowersavingsbalance. + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public boolean isFrameRatePowerSavingsBalanced() { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + return mIsFrameRatePowerSavingsBalanced; + } + return true; + } + + /** * <p> * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount}, * but instead of dimmed, the content behind the window will be blurred (or combined with @@ -5012,6 +5086,7 @@ public interface WindowManager extends ViewManager { out.writeFloat(mDesiredHdrHeadroom); if (sToolkitSetFrameRateReadOnlyFlagValue) { out.writeBoolean(mFrameRateBoostOnTouch); + out.writeBoolean(mIsFrameRatePowerSavingsBalanced); } } @@ -5087,6 +5162,7 @@ public interface WindowManager extends ViewManager { mDesiredHdrHeadroom = in.readFloat(); if (sToolkitSetFrameRateReadOnlyFlagValue) { mFrameRateBoostOnTouch = in.readBoolean(); + mIsFrameRatePowerSavingsBalanced = in.readBoolean(); } } @@ -5430,6 +5506,12 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } + if (sToolkitSetFrameRateReadOnlyFlagValue + && mIsFrameRatePowerSavingsBalanced != o.mIsFrameRatePowerSavingsBalanced) { + mIsFrameRatePowerSavingsBalanced = o.mIsFrameRatePowerSavingsBalanced; + changes |= LAYOUT_CHANGED; + } + return changes; } @@ -5657,6 +5739,11 @@ public interface WindowManager extends ViewManager { sb.append(prefix).append(" frameRateBoostOnTouch="); sb.append(mFrameRateBoostOnTouch); } + if (sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced) { + sb.append(System.lineSeparator()); + sb.append(prefix).append(" dvrrWindowFrameRateHint="); + sb.append(mIsFrameRatePowerSavingsBalanced); + } if (paramsForRotation != null && paramsForRotation.length != 0) { sb.append(System.lineSeparator()); sb.append(prefix).append(" paramsForRotation:"); @@ -6097,52 +6184,65 @@ public interface WindowManager extends ViewManager { * receive batched input event. For those events that are batched, the invocation will happen * once per {@link Choreographer} frame, and other input events will be delivered immediately. * This is different from - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, - * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must - * invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the - * resources when no longer needing to use the {@link SurfaceControlInputReceiver} - * - * @param displayId The display that the SurfaceControl will be placed on. Input will - * only work - * if SurfaceControl is on that display and that display was touched. - * @param surfaceControl The SurfaceControl to register the InputChannel for - * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs - * to ensure the host receives the ANR if any issues with touch on the - * InputChannel - * @param choreographer The Choreographer used for batching. This should match the rendering - * Choreographer. - * @param receiver The SurfaceControlInputReceiver that will receive the input events + * { #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * Looper, SurfaceControlInputReceiver)} in that the input events are received batched. The + * caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up + * the resources when no longer needing to use the {@link SurfaceControlInputReceiver} + * + * @param displayId The display that the SurfaceControl will be placed on. Input + * will only work if SurfaceControl is on that display and that + * display was touched. + * @param surfaceControl The SurfaceControl to register the InputChannel for + * @param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. + * @param choreographer The Choreographer used for batching. This should match the + * rendering Choreographer. + * @param receiver The SurfaceControlInputReceiver that will receive the input + * events + * @return Returns the {@link InputTransferToken} that can be used to transfer touch gesture + * to or from other windows. */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) - default void registerBatchedSurfaceControlInputReceiver(int displayId, - @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, - @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { + @NonNull + default InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, + @NonNull InputTransferToken hostInputTransferToken, + @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, + @NonNull SurfaceControlInputReceiver receiver) { throw new UnsupportedOperationException( "registerBatchedSurfaceControlInputReceiver is not implemented"); } /** * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will - * receive every input event. This is different than calling @link - * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, - * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller - * must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the - * resources when no longer needing to use the {@link SurfaceControlInputReceiver} - * - * @param displayId The display that the SurfaceControl will be placed on. Input will only - * work if SurfaceControl is on that display and that display was - * touched. - * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs - * to ensure the host receives the ANR if any issues with touch on the - * InputChannel - * @param surfaceControl The SurfaceControl to register the InputChannel for - * @param looper The looper to use when invoking callbacks. - * @param receiver The SurfaceControlInputReceiver that will receive the input events - **/ + * receive every input event. This is different than calling + * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * Choreographer, SurfaceControlInputReceiver)} in that the input events are received + * unbatched. + * The caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to + * clean up the resources when no longer needing to use the {@link SurfaceControlInputReceiver} + * + * @param displayId The display that the SurfaceControl will be placed on. Input + * will only work if SurfaceControl is on that display and that + * display was touched. + * @param surfaceControl The SurfaceControl to register the InputChannel for + * @param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. + * @param looper The looper to use when invoking callbacks. + * @param receiver The SurfaceControlInputReceiver that will receive the input + * events. + * @return Returns the {@link InputTransferToken} that can be used to transfer touch gesture + * to or from other windows. + */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) - default void registerUnbatchedSurfaceControlInputReceiver(int displayId, - @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, - @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { + @NonNull + default InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, + @NonNull InputTransferToken hostInputTransferToken, + @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, + @NonNull SurfaceControlInputReceiver receiver) { throw new UnsupportedOperationException( "registerUnbatchedSurfaceControlInputReceiver is not implemented"); } @@ -6152,10 +6252,10 @@ public interface WindowManager extends ViewManager { * specified token. * <p> * Must be called on the same {@link Looper} thread to which was passed to the - * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, + * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, * Choreographer, * SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, Looper, * SurfaceControlInputReceiver)} * * @param surfaceControl The SurfaceControl to remove and unregister the input channel for. @@ -6171,7 +6271,7 @@ public interface WindowManager extends ViewManager { * if the SurfaceControl was registered for input via * { #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, * SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, Looper, * SurfaceControlInputReceiver)}. * <p> * This is helpful for testing to ensure the test waits for the layer to be registered with @@ -6188,6 +6288,70 @@ public interface WindowManager extends ViewManager { } /** + * Transfer the currently in progress touch gesture from the transferFromToken to the + * transferToToken. + * <p><br> + * This requires that the fromToken and toToken are associated with each other. The association + * can be done different ways, depending on how the embedded window is created. + * <ul> + * <li> + * Creating a {@link SurfaceControlViewHost} and passing the host's + * {@link InputTransferToken} for + * {@link SurfaceControlViewHost#SurfaceControlViewHost(Context, Display, InputTransferToken)}. + * </li> + * <li> + * Registering a SurfaceControl for input and passing the host's token to either + * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * Choreographer, SurfaceControlInputReceiver)} or + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, + * SurfaceControl, Looper, SurfaceControlInputReceiver)}. + * </li> + * </ul> + * <p> + * The host is likely to be an {@link AttachedSurfaceControl} so the host token can be + * retrieved via {@link AttachedSurfaceControl#getInputTransferToken()}. + * <p><br> + * Only the window currently receiving touch is allowed to transfer the gesture so if the caller + * attempts to transfer touch gesture from a token that doesn't have touch, it will fail the + * transfer. + * <p><br> + * When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded + * token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} or use the + * value returned from either + * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * Choreographer, SurfaceControlInputReceiver)} or + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, + * Looper, SurfaceControlInputReceiver)} and pass its own token as the transferFromToken. + * <p> + * When the embedded wants to transfer touch gesture to the host, it can pass in its own + * token as the transferFromToken and use the associated host's {@link InputTransferToken} as + * the transferToToken + * <p><br> + * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL + * and does not receive any further input events for this gesture. + * <p> + * The transferred-to window receives an ACTION_DOWN event and then the remainder of the input + * events for this gesture. It does not receive any of the previous events of this gesture that + * the originating window received. + * <p> + * The transferTouchGesture API only works for the current gesture. When a new gesture + * arrives, input dispatcher will do a new round of hit testing. So, if the host window is + * still the first thing that's being touched, then it will receive the new gesture again. It + * will again be up to the host to transfer this new gesture to the embedded. + * + * @param transferFromToken the InputTransferToken for the currently active gesture + * @param transferToToken the InputTransferToken to transfer the gesture to. + * @return Whether the touch stream was transferred. + * @see android.view.SurfaceControlViewHost.SurfacePackage#getInputTransferToken() + * @see AttachedSurfaceControl#getInputTransferToken() + */ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + default boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken, + @NonNull InputTransferToken transferToToken) { + throw new UnsupportedOperationException("transferTouchGesture is not implemented"); + } + + /** * @hide */ default @NonNull IBinder getDefaultToken() { diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 142896346bde..584219a55032 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -839,14 +839,15 @@ public final class WindowManagerGlobal { mTrustedPresentationListener.removeListener(listener); } - void registerBatchedSurfaceControlInputReceiver(int displayId, + InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { IBinder clientToken = new Binder(); + InputTransferToken inputTransferToken = new InputTransferToken(); InputChannel inputChannel = new InputChannel(); try { WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, - clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null, + clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken, surfaceControl.getName(), inputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to create input channel", e); @@ -865,16 +866,18 @@ public final class WindowManagerGlobal { } })); } + return inputTransferToken; } - void registerUnbatchedSurfaceControlInputReceiver(int displayId, + InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { IBinder clientToken = new Binder(); + InputTransferToken inputTransferToken = new InputTransferToken(); InputChannel inputChannel = new InputChannel(); try { WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, - clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null, + clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken, surfaceControl.getName(), inputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to create input channel", e); @@ -892,6 +895,7 @@ public final class WindowManagerGlobal { } })); } + return inputTransferToken; } void unregisterSurfaceControlInputReceiver(SurfaceControl surfaceControl) { @@ -930,6 +934,17 @@ public final class WindowManagerGlobal { return surfaceControlInputReceiverInfo.mClientToken; } + boolean transferTouchGesture(InputTransferToken transferFromToken, + InputTransferToken transferToToken) { + try { + return getWindowManagerService().transferTouchGesture(transferFromToken, + transferToToken); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + return false; + } + private final class TrustedPresentationListener extends ITrustedPresentationListener.Stub { private static int sId = 0; diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 2fb5213279a6..897222879e8f 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -533,22 +533,24 @@ public final class WindowManagerImpl implements WindowManager { mGlobal.unregisterTrustedPresentationListener(listener); } + @NonNull @Override - public void registerBatchedSurfaceControlInputReceiver(int displayId, - @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, - @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { - mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, - new InputTransferToken(hostToken), + public InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId, + @NonNull InputTransferToken hostInputTransferToken, + @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, + @NonNull SurfaceControlInputReceiver receiver) { + return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostInputTransferToken, surfaceControl, choreographer, receiver); } + @NonNull @Override - public void registerUnbatchedSurfaceControlInputReceiver( - int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, - @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { - mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, - new InputTransferToken(hostToken), - surfaceControl, looper, receiver); + public InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId, + @NonNull InputTransferToken hostInputTransferToken, + @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, + @NonNull SurfaceControlInputReceiver receiver) { + return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, + hostInputTransferToken, surfaceControl, looper, receiver); } @Override @@ -563,6 +565,14 @@ public final class WindowManagerImpl implements WindowManager { } @Override + public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken, + @NonNull InputTransferToken transferToToken) { + Objects.requireNonNull(transferFromToken); + Objects.requireNonNull(transferToToken); + return mGlobal.transferTouchGesture(transferFromToken, transferToToken); + } + + @Override public @ScreenRecordingState int addScreenRecordingCallback( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<@ScreenRecordingState Integer> callback) { diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 3f1ae51ef25e..2b2c50725749 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -651,21 +651,6 @@ public class WindowlessWindowManager implements IWindowSession { } @Override - public boolean transferEmbeddedTouchFocusToHost(IWindow window) { - Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" + - " we shouldn't get here!"); - return false; - } - - @Override - public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, - InputTransferToken embeddedInputToken) { - Log.e(TAG, "Received request to transferHostTouchGestureToEmbedded on" - + " WindowlessWindowManager. We shouldn't get here!"); - return false; - } - - @Override public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { Log.e(TAG, "Received request to moveFocusToAdjacentWindow on" + " WindowlessWindowManager. We shouldn't get here!"); diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index fa0052cf664a..749f977f5e50 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -93,6 +94,12 @@ public final class AccessibilityWindowInfo implements Parcelable { */ public static final int TYPE_MAGNIFICATION_OVERLAY = 6; + /** + * Window type: A system window that has the function to control an associated window. + */ + @FlaggedApi(Flags.FLAG_ADD_TYPE_WINDOW_CONTROL) + public static final int TYPE_WINDOW_CONTROL = 7; + /* Special values for window IDs */ /** @hide */ public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE; @@ -873,6 +880,10 @@ public final class AccessibilityWindowInfo implements Parcelable { * @hide */ public static String typeToString(int type) { + if (Flags.addTypeWindowControl() && type == TYPE_WINDOW_CONTROL) { + return "TYPE_WINDOW_CONTROL"; + } + switch (type) { case TYPE_APPLICATION: { return "TYPE_APPLICATION"; diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index a11ac7cb48ad..5b99c71f3a8b 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -102,6 +102,13 @@ flag { flag { namespace: "accessibility" + name: "add_type_window_control" + description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations" + bug: "320445550" +} + +flag { + namespace: "accessibility" name: "update_always_on_a11y_service" description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut." bug: "298869916" diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 57e4e6a2fa5b..9847cb14a70e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -181,6 +181,7 @@ import android.view.PointerIcon; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; +import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewHierarchyEncoder; import android.view.ViewParent; @@ -866,6 +867,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final boolean mUseTextPaddingForUiTranslation; private boolean mUseBoundsForWidth; + private boolean mShiftDrawingOffsetForStartOverhang; @Nullable private Paint.FontMetrics mMinimumFontMetrics; @Nullable private Paint.FontMetrics mLocalePreferredFontMetrics; private boolean mUseLocalePreferredLineHeightForMinimum; @@ -1621,6 +1623,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hasUseBoundForWidthValue = true; break; case com.android.internal.R.styleable + .TextView_shiftDrawingOffsetForStartOverhang: + mShiftDrawingOffsetForStartOverhang = a.getBoolean(attr, false); + break; + case com.android.internal.R.styleable .TextView_useLocalePreferredLineHeightForMinimum: mUseLocalePreferredLineHeightForMinimum = a.getBoolean(attr, false); break; @@ -4922,6 +4928,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @param useBoundsForWidth true for using bounding box for width. false for using advances for * width. * @see #getUseBoundsForWidth() + * @see #setShiftDrawingOffsetForStartOverhang(boolean) + * @see #getShiftDrawingOffsetForStartOverhang() */ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public void setUseBoundsForWidth(boolean useBoundsForWidth) { @@ -4939,6 +4947,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Returns true if using bounding box as a width, false for using advance as a width. * * @see #setUseBoundsForWidth(boolean) + * @see #setShiftDrawingOffsetForStartOverhang(boolean) + * @see #getShiftDrawingOffsetForStartOverhang() * @return True if using bounding box for width, false if using advance for width. */ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @@ -4947,6 +4957,53 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Set true for shifting the drawing x offset for showing overhang at the start position. + * + * This flag is ignored if the {@link #getUseBoundsForWidth()} is false. + * + * If this value is false, the TextView draws text from the zero even if there is a glyph stroke + * in a region where the x coordinate is negative. TextView clips the stroke in the region where + * the X coordinate is negative unless the parents has {@link ViewGroup#getClipChildren()} to + * true. This is useful for aligning multiple TextViews vertically. + * + * If this value is true, the TextView draws text with shifting the x coordinate of the drawing + * bounding box. This prevents the clipping even if the parents doesn't have + * {@link ViewGroup#getClipChildren()} to true. + * + * This value is false by default. + * + * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for showing + * the stroke that is in the region whre the x + * coorinate is negative. + * @see #setUseBoundsForWidth(boolean) + * @see #getUseBoundsForWidth() + */ + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public void setShiftDrawingOffsetForStartOverhang(boolean shiftDrawingOffsetForStartOverhang) { + if (mShiftDrawingOffsetForStartOverhang != shiftDrawingOffsetForStartOverhang) { + mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + } + + /** + * Returns true if shifting the drawing x offset for start overhang. + * + * @see #setShiftDrawingOffsetForStartOverhang(boolean) + * @see #setUseBoundsForWidth(boolean) + * @see #getUseBoundsForWidth() + * @return True if shifting the drawing x offset for start overhang. + */ + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) + public boolean getShiftDrawingOffsetForStartOverhang() { + return mShiftDrawingOffsetForStartOverhang; + } + + /** * Set the minimum font metrics used for line spacing. * * <p> @@ -11001,6 +11058,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener null, boring, mUseBoundsForWidth, + mShiftDrawingOffsetForStartOverhang, getResolvedMinimumFontMetrics()); } @@ -11028,6 +11086,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener effectiveEllipsize, boring, mUseBoundsForWidth, + mShiftDrawingOffsetForStartOverhang, getResolvedMinimumFontMetrics()); } } diff --git a/core/java/android/window/InputTransferToken.java b/core/java/android/window/InputTransferToken.java index 0601b2a79268..e572853e5d5d 100644 --- a/core/java/android/window/InputTransferToken.java +++ b/core/java/android/window/InputTransferToken.java @@ -16,20 +16,40 @@ package android.window; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.os.Binder; import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; +import android.view.Choreographer; +import android.view.SurfaceControl; +import android.view.SurfaceControlInputReceiver; import android.view.SurfaceControlViewHost; +import com.android.window.flags.Flags; + import java.util.Objects; /** * A token that can be used to request focus on or to transfer touch gesture to a * {@link SurfaceControlViewHost} or {@link android.view.SurfaceControl} that has an input channel. - * @hide + * <p> + * The {@link android.view.SurfaceControl} needs to have been registered for input via + * {@link android.view.WindowManager#registerUnbatchedSurfaceControlInputReceiver(int, + * InputTransferToken, SurfaceControl, Looper, SurfaceControlInputReceiver)} or + * {@link android.view.WindowManager#registerBatchedSurfaceControlInputReceiver(int, + * InputTransferToken, SurfaceControl, Choreographer, SurfaceControlInputReceiver)} and the + * returned token can be used to call + * {@link android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)} + * <p> + * For {@link SurfaceControlViewHost}, the token can be retrieved via + * {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} + * + * @see android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken) */ +@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) public final class InputTransferToken implements Parcelable { /** * @hide diff --git a/core/java/android/window/TrustedPresentationThresholds.java b/core/java/android/window/TrustedPresentationThresholds.java index 90f8834b37d1..a30c8fa2c63c 100644 --- a/core/java/android/window/TrustedPresentationThresholds.java +++ b/core/java/android/window/TrustedPresentationThresholds.java @@ -19,15 +19,15 @@ package android.window; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntRange; -import android.annotation.SuppressLint; import android.os.Parcel; import android.os.Parcelable; -import android.view.SurfaceControl; import androidx.annotation.NonNull; import com.android.window.flags.Flags; +import java.util.Objects; + /** * Threshold values that are sent with * {@link android.view.WindowManager#registerTrustedPresentationListener(IBinder, @@ -36,33 +36,53 @@ import com.android.window.flags.Flags; @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) public final class TrustedPresentationThresholds implements Parcelable { /** - * The min alpha the {@link SurfaceControl} is required to have to be considered inside the + * The min alpha the Window is required to have to be considered inside the * threshold. */ @FloatRange(from = 0f, fromInclusive = false, to = 1f) - @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) - @SuppressLint("InternalField") // simple data class - public final float minAlpha; + private final float mMinAlpha; /** - * The min fraction of the SurfaceControl that was presented to the user to be considered + * The min fraction of the Window that was presented to the user to be considered * inside the threshold. */ @FloatRange(from = 0f, fromInclusive = false, to = 1f) - @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) - @SuppressLint("InternalField") // simple data class - public final float minFractionRendered; + private final float mMinFractionRendered; /** - * The time in milliseconds required for the {@link SurfaceControl} to be in the threshold. + * The time in milliseconds required for the Window to be in the threshold. */ @IntRange(from = 1) + private final int mStabilityRequirementMs; + + /** + * The min alpha the Window is required to have to be considered inside the + * threshold. + */ + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public @FloatRange(from = 0f, fromInclusive = false, to = 1f) float getMinAlpha() { + return mMinAlpha; + } + + /** + * The min fraction of the Window that was presented to the user to be considered + * inside the threshold. + */ + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public @FloatRange(from = 0f, fromInclusive = false, to = 1f) float getMinFractionRendered() { + return mMinFractionRendered; + } + + /** + * The time in milliseconds required for the Window to be in the threshold. + */ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) - @SuppressLint("InternalField") // simple data class - public final int stabilityRequirementMs; + public @IntRange(from = 1) int getStabilityRequirementMillis() { + return mStabilityRequirementMs; + } private void checkValid() { - if (minAlpha <= 0 || minFractionRendered <= 0 || stabilityRequirementMs < 1) { + if (mMinAlpha <= 0 || mMinFractionRendered <= 0 || mStabilityRequirementMs < 1) { throw new IllegalArgumentException( "TrustedPresentationThresholds values are invalid"); } @@ -71,23 +91,23 @@ public final class TrustedPresentationThresholds implements Parcelable { /** * Creates a new TrustedPresentationThresholds. * - * @param minAlpha The min alpha the {@link SurfaceControl} is required to + * @param minAlpha The min alpha the Window is required to * have to be considered inside the * threshold. - * @param minFractionRendered The min fraction of the SurfaceControl that was presented + * @param minFractionRendered The min fraction of the Window that was presented * to the user to be considered * inside the threshold. * @param stabilityRequirementMs The time in milliseconds required for the - * {@link SurfaceControl} to be in the threshold. + * Window to be in the threshold. */ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) public TrustedPresentationThresholds( @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minAlpha, @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minFractionRendered, @IntRange(from = 1) int stabilityRequirementMs) { - this.minAlpha = minAlpha; - this.minFractionRendered = minFractionRendered; - this.stabilityRequirementMs = stabilityRequirementMs; + this.mMinAlpha = minAlpha; + this.mMinFractionRendered = minFractionRendered; + this.mStabilityRequirementMs = stabilityRequirementMs; checkValid(); } @@ -95,18 +115,18 @@ public final class TrustedPresentationThresholds implements Parcelable { @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) public String toString() { return "TrustedPresentationThresholds { " - + "minAlpha = " + minAlpha + ", " - + "minFractionRendered = " + minFractionRendered + ", " - + "stabilityRequirementMs = " + stabilityRequirementMs + + "minAlpha = " + mMinAlpha + ", " + + "minFractionRendered = " + mMinFractionRendered + ", " + + "stabilityRequirementMs = " + mStabilityRequirementMs + " }"; } @Override @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeFloat(minAlpha); - dest.writeFloat(minFractionRendered); - dest.writeInt(stabilityRequirementMs); + dest.writeFloat(mMinAlpha); + dest.writeFloat(mMinFractionRendered); + dest.writeInt(mStabilityRequirementMs); } @Override @@ -115,13 +135,34 @@ public final class TrustedPresentationThresholds implements Parcelable { return 0; } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public int hashCode() { + return Objects.hash(mMinAlpha, mMinFractionRendered, mStabilityRequirementMs); + } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TrustedPresentationThresholds that)) { + return false; + } + return mMinAlpha == that.mMinAlpha + && mMinFractionRendered == that.mMinFractionRendered + && mStabilityRequirementMs == that.mStabilityRequirementMs; + } + /** * @hide */ TrustedPresentationThresholds(@NonNull Parcel in) { - minAlpha = in.readFloat(); - minFractionRendered = in.readFloat(); - stabilityRequirementMs = in.readInt(); + mMinAlpha = in.readFloat(); + mMinFractionRendered = in.readFloat(); + mStabilityRequirementMs = in.readInt(); checkValid(); } diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 069affb4c06c..8b3bd974b3fa 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -20,21 +20,6 @@ flag { flag { namespace: "window_surfaces" - name: "get_host_token_api" - description: "Feature flag to associate the host and embedded windows" - is_fixed_read_only: true - bug: "304508760" -} - -flag { - namespace: "window_surfaces" - name: "transfer_gesture_to_embedded" - description: "Enable public API for Window Surfaces" - bug: "287076178" -} - -flag { - namespace: "window_surfaces" name: "delete_capture_display" description: "Delete uses of ScreenCapture#captureDisplay" is_fixed_read_only: true diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index bc63881163f0..ce74848705e4 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -69,4 +69,12 @@ flag { name: "embedded_activity_back_nav_flag" description: "Refines embedded activity back navigation behavior" bug: "293642394" +} + +flag { + namespace: "windowing_sdk" + name: "cover_display_opt_in" + description: "Properties to allow apps and activities to opt-in to cover display rendering" + bug: "312530526" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 3a321e5c26f7..3ec70649294b 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -162,4 +162,5 @@ interface IAppOpsService { int attributionFlags, int attributionChainId); void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId); + List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId); } diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java index 6af03dd29899..2c68203d9cae 100644 --- a/core/java/com/android/internal/content/ReferrerIntent.java +++ b/core/java/com/android/internal/content/ReferrerIntent.java @@ -18,6 +18,7 @@ package com.android.internal.content; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; +import android.os.IBinder; import android.os.Parcel; import java.util.Objects; @@ -29,20 +30,29 @@ public class ReferrerIntent extends Intent { @UnsupportedAppUsage public final String mReferrer; + public final IBinder mCallerToken; + @UnsupportedAppUsage public ReferrerIntent(Intent baseIntent, String referrer) { + this(baseIntent, referrer, /* callerToken */ null); + } + + public ReferrerIntent(Intent baseIntent, String referrer, IBinder callerToken) { super(baseIntent); mReferrer = referrer; + mCallerToken = callerToken; } public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); dest.writeString(mReferrer); + dest.writeStrongBinder(mCallerToken); } ReferrerIntent(Parcel in) { readFromParcel(in); mReferrer = in.readString(); + mCallerToken = in.readStrongBinder(); } public static final Creator<ReferrerIntent> CREATOR = new Creator<ReferrerIntent>() { @@ -60,7 +70,8 @@ public class ReferrerIntent extends Intent { return false; } final ReferrerIntent other = (ReferrerIntent) obj; - return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer); + return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer) + && Objects.equals(mCallerToken, other.mCallerToken); } @Override @@ -68,6 +79,7 @@ public class ReferrerIntent extends Intent { int result = 17; result = 31 * result + filterHashCode(); result = 31 * result + Objects.hashCode(mReferrer); + result = 31 * result + Objects.hashCode(mCallerToken); return result; } } diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index ed943cb2385d..eef6ce7619e8 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -57,7 +57,7 @@ import java.util.concurrent.atomic.AtomicReference; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.LongArrayMultiStateCounter_host") + "com.android.platform.test.ravenwood.nativesubstitution.LongArrayMultiStateCounter_host") public final class LongArrayMultiStateCounter implements Parcelable { /** @@ -65,7 +65,7 @@ public final class LongArrayMultiStateCounter implements Parcelable { */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution" + "com.android.platform.test.ravenwood.nativesubstitution" + ".LongArrayMultiStateCounter_host$LongArrayContainer_host") public static class LongArrayContainer { private static NativeAllocationRegistry sRegistry; diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java index 064609f9dfe4..e5662c7d5145 100644 --- a/core/java/com/android/internal/os/LongMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongMultiStateCounter.java @@ -57,7 +57,7 @@ import libcore.util.NativeAllocationRegistry; */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( - "com.android.hoststubgen.nativesubstitution.LongMultiStateCounter_host") + "com.android.platform.test.ravenwood.nativesubstitution.LongMultiStateCounter_host") public final class LongMultiStateCounter implements Parcelable { private static NativeAllocationRegistry sRegistry; diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java index d433ca3246b6..73df5e8e7b1b 100644 --- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java +++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java @@ -408,6 +408,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, // Derived fields private long mLongVersionCode; private int mLocaleConfigRes; + private boolean mAllowCrossUidActivitySwitchFromBelow; private List<AndroidPackageSplit> mSplits; @@ -1542,6 +1543,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } @Override + public boolean isAllowCrossUidActivitySwitchFromBelow() { + return mAllowCrossUidActivitySwitchFromBelow; + } + + @Override public boolean hasPreserveLegacyExternalStorage() { return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE); } @@ -2199,6 +2205,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } @Override + public ParsingPackage setAllowCrossUidActivitySwitchFromBelow(boolean value) { + mAllowCrossUidActivitySwitchFromBelow = value; + return this; + } + + @Override public PackageImpl setResourceOverlay(boolean value) { return setBoolean(Booleans.OVERLAY, value); } @@ -2656,6 +2668,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, if (!mKnownActivityEmbeddingCerts.isEmpty()) { appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts); } + appInfo.allowCrossUidActivitySwitchFromBelow = mAllowCrossUidActivitySwitchFromBelow; return appInfo; } @@ -3250,6 +3263,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, dest.writeInt(this.uid); dest.writeLong(this.mBooleans); dest.writeLong(this.mBooleans2); + dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow); } public PackageImpl(Parcel in) { @@ -3411,6 +3425,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, this.uid = in.readInt(); this.mBooleans = in.readLong(); this.mBooleans2 = in.readLong(); + this.mAllowCrossUidActivitySwitchFromBelow = in.readBoolean(); assignDerivedFields(); assignDerivedFields2(); diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java index ef106e0268a6..5d185af17d48 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java @@ -374,6 +374,9 @@ public interface ParsingPackage { ParsingPackage setZygotePreloadName(String zygotePreloadName); + ParsingPackage setAllowCrossUidActivitySwitchFromBelow( + boolean allowCrossUidActivitySwitchFromBelow); + ParsingPackage sortActivities(); ParsingPackage sortReceivers(); @@ -518,6 +521,8 @@ public interface ParsingPackage { @Nullable String getZygotePreloadName(); + boolean isAllowCrossUidActivitySwitchFromBelow(); + boolean isBackupAllowed(); boolean isTaskReparentingAllowed(); diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index e0fdbc68a41f..2e6053d3d904 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -2374,8 +2374,10 @@ public class ParsingPackageUtils { .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa)) .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa)) // Non-Config String - .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa)); - // CHECKSTYLE:on + .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa)) + .setAllowCrossUidActivitySwitchFromBelow(bool(true, R.styleable.AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow, sa)); + + // CHECKSTYLE:on //@formatter:on } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 523924566dd7..0dd01e48db0a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -363,6 +363,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mUseDecorContext = false; + private boolean mIsFrameRatePowerSavingsBalanced = true; + /** @see ViewRootImpl#mActivityConfigCallback */ private ActivityConfigCallback mActivityConfigCallback; @@ -2211,6 +2213,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { void onViewRootImplSet(ViewRootImpl viewRoot) { viewRoot.setActivityConfigCallback(mActivityConfigCallback); viewRoot.getOnBackInvokedDispatcher().updateContext(getContext()); + viewRoot.setFrameRatePowerSavingsBalanced(mIsFrameRatePowerSavingsBalanced); mProxyOnBackInvokedDispatcher.setActualDispatcher(viewRoot.getOnBackInvokedDispatcher()); applyDecorFitsSystemWindows(); } @@ -2559,6 +2562,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) { requestFeature(FEATURE_ACTIVITY_TRANSITIONS); } + if (a.hasValue(R.styleable.Window_windowIsFrameRatePowerSavingsBalanced)) { + mIsFrameRatePowerSavingsBalanced = + a.getBoolean(R.styleable.Window_windowIsFrameRatePowerSavingsBalanced, true); + } mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index b5b3a48dacb7..e46b8d7c5fae 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1615,7 +1615,8 @@ public class LockPatternUtils { STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED}) + SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, + SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1641,7 +1642,8 @@ public class LockPatternUtils { /** * Strong authentication is required because the user has been locked out after too many - * attempts. + * attempts using primary auth methods (i.e. PIN/pattern/password) from the lock screen, + * Android Settings, and BiometricPrompt where user authentication is required. */ public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8; @@ -1674,12 +1676,23 @@ public class LockPatternUtils { public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100; /** + * Some authentication is required because adaptive auth has requested to lock device due to + * repeated failed primary auth (i.e. PIN/pattern/password) or biometric auth attempts which + * can come from Android Settings or BiometricPrompt where user authentication is required, + * in addition to from the lock screen. When a risk is determined, adaptive auth will + * proactively prompt the lock screen and will require users to re-enter the device with + * either primary auth or biometric auth (if not prohibited by other flags). + */ + public static final int SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST = 0x200; + + /** * Strong auth flags that do not prevent biometric methods from being accepted as auth. * If any other flags are set, biometric authentication is disabled. */ private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST - | SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; + | SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED + | SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); private final H mHandler; diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java index 096f246b1bab..d430fe32a64e 100644 --- a/core/java/com/android/server/pm/pkg/AndroidPackage.java +++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java @@ -1507,4 +1507,11 @@ public interface AndroidPackage { * @hide */ boolean isVisibleToInstantApps(); + + /** + * @see ApplicationInfo#allowCrossUidActivitySwitchFromBelow + * @see R.styleable#AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow + * @hide + */ + boolean isAllowCrossUidActivitySwitchFromBelow(); } diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp index f6fe3dd842da..96494b16cc21 100644 --- a/core/jni/android_hardware_OverlayProperties.cpp +++ b/core/jni/android_hardware_OverlayProperties.cpp @@ -35,7 +35,6 @@ static struct { jclass clazz; jmethodID ctor; } gOverlayPropertiesClassInfo; - // ---------------------------------------------------------------------------- // OverlayProperties lifecycle // ---------------------------------------------------------------------------- @@ -52,21 +51,21 @@ static jlong android_hardware_OverlayProperties_getDestructor(JNIEnv*, jclass) { // Accessors // ---------------------------------------------------------------------------- -static jboolean android_hardware_OverlayProperties_supportFp16ForHdr(JNIEnv* env, jobject thiz, - jlong nativeObject) { +static jboolean android_hardware_OverlayProperties_isCombinationSupported(JNIEnv* env, jobject thiz, + jlong nativeObject, + jint dataspace, + jint format) { gui::OverlayProperties* properties = reinterpret_cast<gui::OverlayProperties*>(nativeObject); if (properties != nullptr) { for (const auto& i : properties->combinations) { - if (std::find(i.pixelFormats.begin(), i.pixelFormats.end(), - static_cast<int32_t>(HAL_PIXEL_FORMAT_RGBA_FP16)) != + if (std::find(i.pixelFormats.begin(), i.pixelFormats.end(), format) != i.pixelFormats.end() && std::find(i.standards.begin(), i.standards.end(), - static_cast<int32_t>(HAL_DATASPACE_STANDARD_BT709)) != - i.standards.end() && + dataspace & HAL_DATASPACE_STANDARD_MASK) != i.standards.end() && std::find(i.transfers.begin(), i.transfers.end(), - static_cast<int32_t>(HAL_DATASPACE_TRANSFER_SRGB)) != i.transfers.end() && - std::find(i.ranges.begin(), i.ranges.end(), - static_cast<int32_t>(HAL_DATASPACE_RANGE_EXTENDED)) != i.ranges.end()) { + dataspace & HAL_DATASPACE_TRANSFER_MASK) != i.transfers.end() && + std::find(i.ranges.begin(), i.ranges.end(), dataspace & HAL_DATASPACE_RANGE_MASK) != + i.ranges.end()) { return true; } } @@ -88,7 +87,7 @@ static jlong android_hardware_OverlayProperties_createDefault(JNIEnv* env, jobje gui::OverlayProperties* overlayProperties = new gui::OverlayProperties; gui::OverlayProperties::SupportedBufferCombinations combination; combination.pixelFormats = {HAL_PIXEL_FORMAT_RGBA_8888}; - combination.standards = {HAL_DATASPACE_BT709}; + combination.standards = {HAL_DATASPACE_STANDARD_BT709}; combination.transfers = {HAL_DATASPACE_TRANSFER_SRGB}; combination.ranges = {HAL_DATASPACE_RANGE_FULL}; overlayProperties->combinations.emplace_back(combination); @@ -153,8 +152,8 @@ const char* const kClassPathName = "android/hardware/OverlayProperties"; // clang-format off static const JNINativeMethod gMethods[] = { { "nGetDestructor", "()J", (void*) android_hardware_OverlayProperties_getDestructor }, - { "nSupportFp16ForHdr", "(J)Z", - (void*) android_hardware_OverlayProperties_supportFp16ForHdr }, + { "nIsCombinationSupported", "(JII)Z", + (void*) android_hardware_OverlayProperties_isCombinationSupported }, { "nSupportMixedColorSpaces", "(J)Z", (void*) android_hardware_OverlayProperties_supportMixedColorSpaces }, { "nWriteOverlayPropertiesToParcel", "(JLandroid/os/Parcel;)V", @@ -172,6 +171,5 @@ int register_android_hardware_OverlayProperties(JNIEnv* env) { gOverlayPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gOverlayPropertiesClassInfo.ctor = GetMethodIDOrDie(env, gOverlayPropertiesClassInfo.clazz, "<init>", "(J)V"); - return err; } diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto index 8c3304137904..d9ed911515ba 100644 --- a/core/proto/android/app/appstartinfo.proto +++ b/core/proto/android/app/appstartinfo.proto @@ -39,4 +39,5 @@ message ApplicationStartInfoProto { optional AppStartStartType start_type = 9; optional bytes start_intent = 10; optional AppStartLaunchMode launch_mode = 11; + optional bool was_force_stopped = 12; } diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index b9905e8cf446..b7408a4da381 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -113,6 +113,7 @@ message ApplicationInfoProto { optional int32 enable_gwp_asan = 19; optional int32 enable_memtag = 20; optional bool native_heap_zero_init = 21; + optional bool allow_cross_uid_activity_switch_from_below = 22; } optional Detail detail = 17; repeated string overlay_paths = 18; diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto index 9359528b613b..e368c6a98a75 100644 --- a/core/proto/android/hardware/sensorprivacy.proto +++ b/core/proto/android/hardware/sensorprivacy.proto @@ -91,6 +91,9 @@ message SensorPrivacyIndividualEnabledSensorProto { enum StateType { ENABLED = 1; DISABLED = 2; + AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS = 3; + AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS = 4; + AUTO_DRIVER_ASSISTANCE_APPS = 5; } // DEPRECATED @@ -134,4 +137,4 @@ message SensorPrivacyToggleSourceProto { // Source for which sensor privacy was toggled. optional Source source = 1; -}
\ No newline at end of file +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e4e8f7ef5247..71f06f1106de 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -836,6 +836,7 @@ <protected-broadcast android:name="android.intent.action.PROFILE_AVAILABLE" /> <protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" /> <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" /> + <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -1733,6 +1734,16 @@ android:protectionLevel="signature" android:featureFlag="com.android.internal.camera.flags.camera_hsum_permission" /> + + <!-- @SystemApi Allows camera access of allowlisted driver assistance apps + to be controlled separately. + <p> Not for use by third-party applications. + @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") + @hide + --> + <permission android:name="android.permission.CAMERA_PRIVACY_ALLOWLIST" + android:protectionLevel="signature|privileged" /> + <!-- ====================================================================== --> <!-- Permissions for accessing the device sensors --> <!-- ====================================================================== --> @@ -3679,6 +3690,14 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING" android:protectionLevel="internal|role" /> + <!-- Allows an application to use audit logging API. + @hide + @SystemApi + @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" + android:protectionLevel="internal|role" /> + <!-- Allows an application to set policy related to system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. @@ -3782,6 +3801,14 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK" android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to theft detection. + @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") + @hide + @SystemApi + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION" + android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to system apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. @@ -3819,6 +3846,24 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS" android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to block package uninstallation. + @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL" + android:protectionLevel="internal|role" /> + + <!-- Allows an application to manage policy related to camera toggle. + @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE" + android:protectionLevel="internal|role" /> + + <!-- Allows an application to manage policy related to microphone toggle. + @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE" + android:protectionLevel="internal|role" /> + <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index eebd765705ee..da67efee4842 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk is gestaaf"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesig is gestaaf"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesig is gestaaf; druk asseblief bevestig"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Vingerafdrukhardeware is nie beskikbaar nie."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan nie vingerafdruk opstel nie"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vingerafdrukopstelling het uitgetel. Probeer weer."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukhandeling is gekanselleer."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogings. Gebruik eerder skermslot."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogings. Gebruik eerder skermslot."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan nie vingerafdruk verwerk nie. Probeer weer."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan nie vingerafdruksensor gebruik nie. Besoek \'n verskaffer wat herstelwerk doen"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Hierdie toetstel het nie \'n vingerafdruksensor nie"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/af-skakelaar is gedruk"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index ea6efdb1c0c3..4a7f6f3b7976 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ፊት ተረጋግጧል"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"የጣት አሻራ ሃርድዌር አይገኝም።"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"የጣት አሻራን ማዋቀር አልተቻለም"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"የጣት አሻራ ውቅረት ጊዜው አብቅቷል። እንደገና ይሞክሩ።"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"የጣት አሻራን ማሰናዳት አልተቻለም። እንደገና ይሞክሩ።"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"የጣት አሻራ ዳሳሽን መጠቀም አይቻልም። የጥገና አገልግሎት ሰጪን ይጎብኙ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"የኃይል አዝራር ተጭኗል"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 88adffabe394..b767c5704cef 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -670,18 +670,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"تمّت مصادقة الوجه"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"جهاز بصمة الإصبع غير متاح."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"يتعذّر إعداد بصمة الإصبع."</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"انتهت مهلة إعداد بصمة الإصبع. يُرجى إعادة المحاولة."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"تتعذّر معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"لا يمكن استخدام أداة استشعار بصمة الإصبع. يُرجى التواصل مع مقدِّم خدمات إصلاح."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"لا يحتوي هذا الجهاز على جهاز استشعار بصمات الأصابع."</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"تم الضغط على زر التشغيل"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 0be253222232..19d8357644d8 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰিব নোৱাৰি"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিংগাৰপ্ৰিণ্ট ছেটআপ কৰাৰ সময় উকলি গৈছে। পুনৰ চেষ্টা কৰক।"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিংগাৰপ্ৰিণ্ট চিনাক্তকৰণ প্ৰক্ৰিয়া কৰিব পৰা নাই। পুনৰ চেষ্টা কৰক।"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ ব্যৱহাৰ কৰিব নোৱাৰি। মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাৱাৰ বুটাম টিপা হৈছে"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 63efc6655555..b82ffdced01a 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Üz doğrulandı"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmaq izi üçün avadanlıq yoxdur."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmaq izini ayarlamaq mümkün deyil"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmaq izi ayarlama vaxtı bitib. Yenidən cəhd edin."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmaq izi əməliyyatı ləğv edildi."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmaq izini emal etmək mümkün deyil. Yenidən cəhd edin."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Barmaq izi sensorundan istifadə etmək mümkün deyil. Təmir provayderini ziyarət edin"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda barmaq izi sensoru yoxdur"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Qidalanma düyməsi basılıb"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 2cb924efe50c..655d1d81efe3 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otiske prstiju nije dostupan."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Podešavanje otiska prsta nije uspelo"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vreme za podešavanje otiska prsta je isteklo. Probajte ponovo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja sa otiskom prsta je otkazana."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju sa otiskom prsta."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrađivanje otiska prsta nije uspelo. Probajte ponovo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ne možete da koristite senzor za otisak prsta. Posetite dobavljača za popravke"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuto je dugme za uključivanje"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 8815ae2f5092..d36c7d1b8b66 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Твар распазнаны"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не ўдалося захаваць адбітак пальца"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Наладжванне адбітка пальца не завершана. Паўтарыце спробу."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Аперацыя з адбіткамі пальцаў скасавана."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не ўдалося апрацаваць адбітак пальца. Паўтарыце спробу."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не ўдалося скарыстаць сканер адбіткаў пальцаў. Звярніцеся ў сэрвісны цэнтр."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На гэтай прыладзе няма сканера адбіткаў пальцаў"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Націснута кнопка сілкавання"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index b427349c51f5..24d5d293d6f3 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е удостоверено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардуерът за отпечатъци не е налице."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се настрои отпечатък"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Настройването на отпечатък не завърши навреме. Опитайте отново."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцията за отпечатък е анулирана."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Отпечатъкът не може да бъде обработен. Опитайте отново."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Сензорът за отпечатъци не може да се използва. Посетете оторизиран сервиз."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Това устройство няма сензор за отпечатъци"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Бутонът за захранване е натиснат"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 8da77ae1f370..f7f63cc4cc6c 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ফেস যাচাই করা হয়েছে"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার অনুপলব্ধ৷"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"আঙ্গুলের ছাপ সেট-আপ করতে পারছি না"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিঙ্গারপ্রিন্ট সেট-আপ করার সময় সীমা পেরিয়ে গেছে। আবার চেষ্টা করুন।"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিঙ্গারপ্রিন্ট প্রসেস করা যায়নি। আবার চেষ্টা করুন।"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"আঙ্গুলের ছাপের সেন্সর ব্যবহার করা যাচ্ছে না। একজন মেরামতি মিস্ত্রির কাছে যান"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইসে আঙ্গুলের ছাপের সেন্সর নেই"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাওয়ার বোতাম প্রেস করা হয়েছে"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 819695f91e02..b433f7762dd1 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je provjereno"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je provjereno, pritisnite dugme za potvrdu"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nije moguće postaviti otisak prsta"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja s otiskom prsta je otkazana."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju s otiskom prsta."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nije moguće obraditi otisak prsta. Pokušajte ponovo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nije moguće koristiti senzor za otisak prsta. Posjetite pružaoca usluga za popravke"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Dugme za uključivanje je pritisnuto"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 785fd27b0d19..f93d6bb9562f 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari d\'empremtes digitals no està disponible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"No es pot configurar l\'empremta digital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Temps d\'espera esgotat per configurar l\'empremta digital. Torna-ho a provar."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Massa intents. Utilitza el bloqueig de pantalla."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Massa intents. Utilitza el bloqueig de pantalla."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No es pot processar l\'empremta digital. Torna-ho a provar."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No es pot utilitzar el sensor d\'empremtes digitals. Visita un proveïdor de reparacions."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aquest dispositiu no té sensor d\'empremtes digitals"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"S\'ha premut el botó d\'engegada"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index d95536280176..7edc6b46c623 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Obličej byl ověřen"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Není k dispozici hardware ke snímání otisků prstů."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Otisk prstu se nepodařilo nastavit"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Časový limit nastavení otisku prstu vypršel. Zkuste to znovu."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operace otisku prstu byla zrušena."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Uživatel operaci s otiskem prstu zrušil."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Snímač otisků prstů nelze použít. Navštivte servis"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zařízení nemá snímač otisků prstů"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bylo stisknut vypínač"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 65f4c6d9ad58..b5938fd09d96 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansigtet er godkendt"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardwaren til fingeraftryk er ikke tilgængelig."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingeraftrykket kan ikke gemmes"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurationen af fingeraftryk fik timeout. Prøv igen."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeraftrykshandlingen blev annulleret."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeraftrykshandlingen blev annulleret af brugeren."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingeraftrykket kan ikke behandles. Prøv igen."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykssensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Fingeraftrykssensoren kan ikke bruges. Få den repareret"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enhed har ingen fingeraftrykslæser"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Der blev trykket på afbryderknappen"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 286c1d6be80b..59c7d4a0d389 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesicht authentifiziert"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerabdruckhardware nicht verfügbar"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingerabdruck konnte nicht eingerichtet werden"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Zeitüberschreitung bei Fingerabdruckeinrichtung. Versuch es noch einmal."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerabdruckvorgang abgebrochen"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingerabdruck kann nicht verarbeitet werden. Versuch es noch einmal."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Der Fingerabdrucksensor kann nicht verwendet werden. Suche einen Reparaturdienstleister auf."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dieses Gerät hat keinen Fingerabdrucksensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ein-/Aus-Taste wurde gedrückt"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 1de2bc6b9b39..762bd52bed0c 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Έγινε έλεγχος ταυτότητας προσώπου"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Δεν είναι δυνατή η ρύθμιση του δακτυλικού αποτυπώματος"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Λήξη χρονικού ορίου ρύθμισης δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Δεν είναι δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Δεν είναι δυνατή η χρήση του αισθητήρα δακτυλικών αποτυπωμάτων. Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Το κουμπί λειτουργίας πατήθηκε"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string> @@ -1756,7 +1762,7 @@ <string name="user_switched" msgid="7249833311585228097">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="user_switching_message" msgid="1912993630661332336">"Εναλλαγή σε <xliff:g id="NAME">%1$s</xliff:g>…"</string> <string name="user_logging_out_message" msgid="7216437629179710359">"Αποσύνδεση <xliff:g id="NAME">%1$s</xliff:g>…"</string> - <string name="owner_name" msgid="8713560351570795743">"Κάτοχος"</string> + <string name="owner_name" msgid="8713560351570795743">"Κάτοχο"</string> <string name="guest_name" msgid="8502103277839834324">"Επισκέπτης"</string> <string name="error_message_title" msgid="4082495589294631966">"Σφάλμα"</string> <string name="error_message_change_not_allowed" msgid="843159705042381454">"Αυτή η αλλαγή δεν επιτρέπεται από το διαχειριστή σας"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 0b752f69b619..719c4232a52d 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 0dbb2ef21795..e317067c53f9 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation canceled."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation canceled by user."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 4a7ba92244f2..0e58b1d17615 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index a7332c66878d..3c6b6712b84b 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 8f709048d25a..34a020f4395a 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation canceled."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation canceled by user."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index b6edfd7e852d..7fb0d1f446d1 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Se autenticó el rostro"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware para detectar huellas dactilares no está disponible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella dactilar"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Se agotó el tiempo de espera para configurar la huella dactilar. Vuelve a intentarlo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Se canceló la operación de huella dactilar."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario canceló la operación de huella dactilar."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella dactilar. Vuelve a intentarlo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No se puede usar el sensor de huellas dactilares. Consulta a un proveedor de reparaciones."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo no tiene sensor de huellas dactilares"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se presionó el botón de encendido"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella dactilar"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index baa445e1eec3..7d0c4e0318f4 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware de huella digital no está disponible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella digital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiempo de espera para configurar la huella digital agotado. Inténtalo de nuevo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Se ha cancelado la operación de huella digital."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario ha cancelado la operación de huella digital."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Usa el bloqueo de pantalla."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Usa el bloqueo de pantalla."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella digital. Inténtalo de nuevo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No se puede usar el sensor de huellas digitales. Visita un proveedor de reparaciones."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"El dispositivo no tiene ningún sensor de huellas digitales"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se ha pulsado el botón de encendido"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 3cb3605403c8..250341f07a82 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Nägu on autenditud"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sõrmejälje riistvara pole saadaval."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sõrmejälge ei saa seadistada"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sõrmejälje seadistamine aegus. Proovige uuesti."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Sõrmejälje toiming tühistati."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kasutaja tühistas sõrmejälje kasutamise."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sõrmejälge ei õnnestu töödelda. Proovige uuesti."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sõrmejäljeandurit ei saa kasutada. Külastage remonditeenuse pakkujat"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Selles seadmes pole sõrmejäljeandurit"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vajutati toitenuppu"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 8e1e1864bc6f..3c5377989497 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autentifikatu da aurpegia"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hatz-marken hardwarea ez dago erabilgarri."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ezin da konfiguratu hatz-marka"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hatz-marka konfiguratzeko denbora-muga gainditu da. Saiatu berriro."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Hatz-markaren eragiketa bertan behera utzi da."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ezin da prozesatu hatz-marka. Saiatu berriro."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ezin da erabili hatz-marken sentsorea. Jarri harremanetan konponketak egiten dituen hornitzaile batekin."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Gailu honek ez du hatz-marken sentsorerik"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Etengailua sakatu da"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index bba6a1b86db5..640f93f73563 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالتسنجی شد"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالتسنجی شد"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالتسنجی شد، لطفاً تأیید را فشار دهید"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سختافزار اثرانگشت در دسترس نیست."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"اثر انگشت راهاندازی نشد"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"مهلت تنظیم اثر انگشت بهپایان رسید. دوباره امتحان کنید."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"عملکرد اثر انگشت لغو شد."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"کاربر عملیات اثر انگشت را لغو کرد"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاشها از حد مجاز بیشتر شده است. بهجای آن از قفل صفحه استفاده کنید."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاشهای بیشازحد. حالا از قفل صفحه استفاده کنید."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"اثر انگشت پردازش نشد. دوباره امتحان کنید."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر بهطور موقت غیرفعال است."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"امکان استفاده از حسگر اثر انگشت وجود ندارد. به ارائهدهنده خدمات تعمیر مراجعه کنید"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"این دستگاه حسگر اثر انگشت ندارد"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"دکمه روشن/خاموش فشار داده شد"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 6650570af968..18b9ee2a5a64 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Kasvot tunnistettu"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sormenjälkilaitteisto ei ole käytettävissä."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sormenjälkeä ei voi ottaa käyttöön"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sormenjäljen käyttöönotto aikakatkaistu. Yritä uudelleen."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Sormenjälkitoiminto peruutettiin."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Käyttäjä peruutti sormenjälkitoiminnon."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sormenjälkeä ei voida käsitellä. Yritä uudelleen."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sormenjälkitunnistinta ei voi käyttää. Ota yhteys korjauspalveluun"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Laitteessa ei ole sormenjälkitunnistinta."</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Virtapainiketta on painettu"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index c6cda6598ee3..1eb9eb21c842 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Lecteur d\'empreintes digitales indisponible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai dépassé pour configurer l\'empreinte digitale. Réessayez."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Empreinte digitale non traitable. Réessayez."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le capteur d\'empreintes digitales. Consultez un fournisseur de services de réparation"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Cet appareil ne possède pas de capteur d\'empreintes digitales"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vous avez appuyé sur l\'interrupteur"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index b15ca166b1b2..c349b1cf1774 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Matériel d\'empreinte digitale indisponible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai de configuration de l\'empreinte dépassé. Réessayez."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale annulée."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossible de reconnaître l\'empreinte digitale. Réessayez."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le lecteur d\'empreinte digitale. Contactez un réparateur"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bouton Marche/Arrêt appuyé"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index ec2833dec526..2cc80d5cf9a7 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autenticouse a cara"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impresión dixital non dispoñible."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Non se puido configurar a impresión dixital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Esgotouse o tempo para configurar a impresión dixital. Téntao de novo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Cancelouse a operación da impresión dixital."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"O usuario cancelou a operación da impresión dixital."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Houbo demasiados intentos. Mellor usa o bloqueo de pantalla."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Mellor usa o bloqueo de pantalla."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Non se pode procesar a impresión dixital Téntao de novo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Non se puido usar o sensor de impresión dixital. Visita un provedor de reparacións"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo non ten sensor de impresión dixital"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Premeuse o botón de acendido"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 5cb77d8fdfed..1ef8c4703205 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ચહેરા પ્રમાણિત"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ફિંગરપ્રિન્ટનું સેટઅપ કરી શકતા નથી"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ફિંગરપ્રિન્ટનું સેટઅપ કરવાનો સમય સમાપ્ત થઈ ગયો. ફરી પ્રયાસ કરો."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ઘણા બધા પ્રયાસો. તેને બદલે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ઘણા બધા પ્રયાસો. વિકલ્પ તરીકે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ફિંગરપ્રિન્ટની પ્રક્રિયા કરી શકતા નથી. ફરી પ્રયાસ કરો."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ફિંગરપ્રિન્ટ સેન્સરનો ઉપયોગ કરી શકાતો નથી. રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"પાવર બટન દબાવવામાં આવ્યું"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index d51146933785..bd49843657a7 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"फ़िंगरप्रिंट सेट अप नहीं किया जा सका"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फ़िंगरप्रिंट सेटअप करने का समय खत्म हो गया. फिर से कोशिश करें."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"कई बार कोशिश की जा चुकी है. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"इससे ज़्यादा बार कोशिश नहीं की जा सकती. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फ़िंगरप्रिंट की पहचान नहीं की जा सकी. फिर से कोशिश करें."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फ़िंगरप्रिंट सेंसर इस्तेमाल नहीं किया जा सकता. रिपेयर की सेवा देने वाली कंपनी से संपर्क करें"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन दबाने की वजह से गड़बड़ी हुई"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 636baa0be42b..f1e9bd6d0636 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je autentificirano"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Postavljanje otiska prsta nije uspjelo"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vrijeme za postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja otiska prsta otkazana je."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Radnju s otiskom prsta otkazao je korisnik."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Senzor otiska prsta ne može se koristiti. Posjetite davatelja usluga popravaka"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor otiska prsta"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuta je tipka za uključivanje/isključivanje"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 2c9f7cce21c6..26c607788ac1 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Arc hitelesítve"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Az ujjlenyomathoz szükséges hardver nem érhető el."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nem sikerült beállítani az ujjlenyomatot"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Lejárt az ujjlenyomat-beállítás időkorlátja. Próbálkozzon újra."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Ujjlenyomattal kapcsolatos művelet megszakítva"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Túl sokszor próbálkozott. Használja inkább a képernyőzárat."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Túl sok próbálkozás. Használja inkább a képernyőzárat."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nem lehet használni az ujjlenyomat-érzékelőt. Keresse fel a szervizt."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bekapcsológomb megnyomva"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 904f47c0246d..e82dc708de73 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Դեմքը ճանաչվեց"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Մատնահետքի սարքն անհասանելի է:"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Հնարավոր չէ կարգավորել մատնահետքը"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Մատնահետքի կարգավորման ժամանակը սպառվել է։ Նորից փորձեք։"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Չի հաջողվում մշակել մատնահետքը։ Նորից փորձեք։"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Մատնահետքերի սկաները հնարավոր չէ օգտագործել։ Այցելեք սպասարկման կենտրոն։"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Սարքը չունի մատնահետքի սկաներ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Սեղմվել է սնուցման կոճակը"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 9df1d3aaa9ec..52d1463c7e24 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah diautentikasi"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware sidik jari tidak tersedia."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyiapkan sidik jari"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Waktu penyiapan sidik jari habis. Coba lagi."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operasi sidik jari dibatalkan."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operasi sidik jari dibatalkan oleh pengguna."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses sidik jari. Coba lagi."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tidak dapat menggunakan sensor sidik jari. Kunjungi penyedia reparasi"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Perangkat ini tidak memiliki sensor sidik jari"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tombol daya ditekan"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 5f0e0ed43c82..81e5a62d5c38 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Andlit staðfest"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingrafarsvélbúnaður ekki til staðar."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ekki er hægt að setja upp fingrafar"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingrafarsuppsetning rann út á tíma. Reyndu aftur."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Hætt við fingrafarsaðgerð."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Notandi hætti við að nota fingrafar."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Of margar tilraunir. Notaðu skjálás í staðinn."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Of margar tilraunir. Notaðu skjálás í staðinn."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ekki tekst að vinna úr fingrafari. Reyndu aftur."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ekki er hægt að nota fingrafaralesara. Þú verður að fara með hann á verkstæði"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Þetta tæki er ekki með fingrafaralesara"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ýtt á aflrofa"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 014ac499a9d5..e5a8b2506366 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impronta autenticata"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Volto autenticato"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Volto autenticato, premi Conferma"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware per l\'impronta non disponibile."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossibile configurare l\'impronta"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Timeout configurazione impronta. Riprova."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operazione associata all\'impronta annullata."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Troppi tentativi. Usa il blocco schermo."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Troppi tentativi. Usa il blocco schermo."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossibile elaborare l\'impronta. Riprova."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossibile usare il sensore di impronte digitali. Contatta un fornitore di servizi di riparazione"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Questo dispositivo non è dotato di sensore di impronte"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tasto di accensione premuto"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index daac1ca5f0bc..1e040c19bd89 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"זיהוי הפנים בוצע"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"החומרה לזיהוי טביעות אצבעות אינה זמינה."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"לא ניתן להגדיר טביעת אצבע"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"הזמן שהוקצב להגדרה של טביעת האצבע פג. יש לנסות שוב."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"פעולת טביעת האצבע בוטלה."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה על ידי המשתמש."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"לא ניתן לעבד את טביעת האצבע. יש לנסות שוב."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"לא ניתן להשתמש בחיישן טביעות האצבע. צריך ליצור קשר עם ספק תיקונים"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"במכשיר הזה אין חיישן טביעות אצבע"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"לחצן ההפעלה נלחץ"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index c9005dc286d9..f4382c4e2505 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"顔を認証しました"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋認証ハードウェアは使用できません。"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"指紋を設定できません"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"指紋認証センサーを使用できません。修理業者に調整を依頼してください"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"このデバイスには指紋認証センサーがありません"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"電源ボタンが押されました"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 8cd3c941e06c..35f885b913f3 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"თითის ანაბეჭდი ავტორიზებულია"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"სახე ავტორიზებულია"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"თითის ანაბეჭდის აპარატურა არ არის ხელმისაწვდომი."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"თითის ანაბეჭდის დაყენება ვერ ხერხდება"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"თითის ანაბეჭდის დაყენების დრო ამოიწურა. ცადეთ ხელახლა."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"თითის ანაბეჭდის აღების ოპერაცია გაუქმდა."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"თითის ანაბეჭდის დამუშავება შეუძლებელია. ცადეთ ხელახლა."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"თითის ანაბეჭდის სენსორის გამოყენება ვერ ხერხდება. ეწვიეთ შეკეთების სერვისის პროვაიდერს"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ჩართვის ღილაკზე დაეჭირა"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index a8299b7cd6ab..1e7a73b7c57b 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -153,26 +153,16 @@ <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string> <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string> <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: қайта бағытталған жоқ."</string> - <!-- no translation found for scCellularNetworkSecurityTitle (90330018476923559) --> - <skip /> - <!-- no translation found for scCellularNetworkSecuritySummary (8659128412709908263) --> - <skip /> - <!-- no translation found for scIdentifierDisclosureIssueTitle (3737384845335568193) --> - <skip /> - <!-- no translation found for scIdentifierDisclosureIssueSummary (3870743771498510600) --> - <skip /> - <!-- no translation found for scNullCipherIssueEncryptedTitle (8426373579673205292) --> - <skip /> - <!-- no translation found for scNullCipherIssueEncryptedSummary (6437468449554283998) --> - <skip /> - <!-- no translation found for scNullCipherIssueNonEncryptedTitle (2069674849204163569) --> - <skip /> - <!-- no translation found for scNullCipherIssueNonEncryptedSummary (3577092996366374833) --> - <skip /> - <!-- no translation found for scNullCipherIssueActionSettings (8378372959891478470) --> - <skip /> - <!-- no translation found for scNullCipherIssueActionLearnMore (7896642417214757769) --> - <skip /> + <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"Ұялы желі қауіпсіздігі"</string> + <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"Параметрлерді қарап шығу"</string> + <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"Құрылғы идентификаторы пайдаланылды"</string> + <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"Желі (<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> байланысы) құрылғының бірегей идентификаторын (IMSI) <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g> және <xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g> аралығында <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> рет жазып алды."</string> + <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"\"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" желісіне қосылу шифрланды"</string> + <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"Қазір қауіпсіздеу ұялы желіге қосылып тұрсыз."</string> + <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"\"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" желісіне қосылу шифрланбаған"</string> + <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"Шифрланбаған ұялы желіге қосылғансыз. Қоңырауларды, хабарлар мен деректерді басқалар қолға түсіруі мүмкін."</string> + <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"Ұялы желі қауіпсіздігінің параметрлері"</string> + <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Толық ақпарат"</string> <string name="fcComplete" msgid="1080909484660507044">"Функция коды толық."</string> <string name="fcError" msgid="5325116502080221346">"Байланыс мәселесі немесе функция коды жарамсыз."</string> <string name="httpErrorOk" msgid="6206751415788256357">"Жарайды"</string> @@ -390,10 +380,8 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ұялы таратылым хабарлары алынғаннан кейін, олардың бағытын өзгерту үшін қолданбаға ұялы таратылым модулімен байланыстыруға мүмкіндік береді. Ұялы таратылым ескертулері кей аймақтарда төтенше жағдайлар туралы хабарлау үшін беріледі. Төтенше жағдай туралы ұялы таратылым хабары алынғаннан кейін, зиянды қолданбалар құрылғы жұмысына кедергі келтіруі мүмкін."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Қазіргі қоңырауларды басқару"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Қолданбаға құрылғыдағы қазіргі қоңыраулар туралы мәліметтерді көруге және басқаруға мүмкіндік береді."</string> - <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> - <skip /> - <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> - <skip /> + <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Белгілі соңғы ұялы байланыс идентификаторын пайдалану."</string> + <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Қолданба телефония қызметі берген белгілі соңғы ұялы байланыс идентификаторын пайдалануға рұқсат алады."</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ұялы хабар тарату хабарларын оқу"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Қолданбаға ұялы таратылым хабарларын оқу мүмкіндігін береді. Ұялы таратылым дабылдары кейбір аймақтарда төтенше жағдай туралы ескерту үшін қолданылады. Төтенше ұялы хабарлар келгенде залалды қолданбалар құрылғының жұмысына кедергі жасауы мүмкін."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"жазылған ағындарды оқу"</string> @@ -565,10 +553,8 @@ <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Қолданбаға телефонның инфрақызыл қабылдағышын қолдану мүмкіндігін береді."</string> <string name="permlab_setWallpaper" msgid="6959514622698794511">"артқы фонды орнату"</string> <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Қолданбаға жүйелік экранның артқы фонын орнатуға рұқсат береді."</string> - <!-- no translation found for permlab_accessHiddenProfile (8607094418491556823) --> - <skip /> - <!-- no translation found for permdesc_accessHiddenProfile (1543153202481009676) --> - <skip /> + <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Жасырын профильдерге кіру"</string> + <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"Қолданбаға жасырын профильдерге кіру рұқсатын береді."</string> <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"артқы фон өлшемін реттеу"</string> <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Қолданбаға жүйелік экранның артқы фонының өлшемі туралы кеңестерді орнатуға рұқсат береді."</string> <string name="permlab_setTimeZone" msgid="7922618798611542432">"уақыт аймағын реттеу"</string> @@ -680,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Бет танылды"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Саусақ ізі жабдығы қолжетімді емес."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Саусақ ізін орнату мүмкін емес."</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Саусақ ізін реттеу уақыты өтіп кетті. Қайталап көріңіз."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Саусақ ізі операциясынан бас тартылған."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Саусақ ізін өңдеу мүмкін емес. Қайталап көріңіз."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Саусақ ізін оқу сканерін пайдалану мүмкін емес. Жөндеу қызметіне барыңыз."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бұл құрылғыда саусақ ізін оқу сканері жоқ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Қуат түймесі басулы."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-саусақ"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string> @@ -837,10 +829,8 @@ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Қолданбаға жиілігі 200 Гц-тен жоғары датчик деректерінің үлгісін таңдауға рұқсат береді."</string> <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"қолданбаны автоматты түрде жаңарту"</string> <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Бұған дейін орнатылған қолданбаның автоматты түрде жаңартылуына мүмкіндік береді."</string> - <!-- no translation found for permlab_writeVerificationStateE2eeContactKeys (3990742344778360457) --> - <skip /> - <!-- no translation found for permdesc_writeVerificationStateE2eeContactKeys (8453156829747427041) --> - <skip /> + <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"тура шифрлаумен қорғалған және басқа қолданбаларға тиесілі контакт кілттерін тексеру күйлерін жаңарту"</string> + <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"Қолданбаға тура шифрлаумен қорғалған және басқа қолданбаларға тиесілі контакт кілттерін тексеру күйлерін жаңартуға рұқсат береді."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран бекітпесінің құпия сөздерінің және PIN кодтарының ұзындығын және оларда рұқсат етілген таңбаларды басқару."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әрекеттерін бақылау"</string> @@ -2402,12 +2392,8 @@ <string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> - <!-- no translation found for satellite_notification_title (4026338973463121526) --> - <skip /> - <!-- no translation found for satellite_notification_summary (5207364139430767162) --> - <skip /> - <!-- no translation found for satellite_notification_open_message (4149234979688273729) --> - <skip /> - <!-- no translation found for satellite_notification_how_it_works (3132069321977520519) --> - <skip /> + <string name="satellite_notification_title" msgid="4026338973463121526">"Жерсерік қызметіне автоматты түрде қосылды"</string> + <string name="satellite_notification_summary" msgid="5207364139430767162">"Мобильдік не Wi-Fi желісіне қосылмастан хабар жібере аласыз және ала аласыз."</string> + <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string> + <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string> </resources> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 27d9a06bc731..37a4f817fd13 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"បានផ្ទៀងផ្ទាត់មុខ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់មុខ សូមចុចបញ្ជាក់"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"មិនអាចរៀបចំស្នាមម្រាមដៃបានទេ"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ការរៀបចំស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ប្រតិបត្តិការស្នាមម្រាមដៃត្រូវបានបោះបង់ដោយអ្នកប្រើប្រាស់។"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ព្យាយាមច្រើនដងពេក។ សូមប្រើការចាក់សោអេក្រង់ជំនួសវិញ។"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ព្យាយាមច្រើនដងពេក។ សូមប្រើការចាក់សោអេក្រង់ជំនួសវិញ។"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិនមានការចុះឈ្មោះស្នាមម្រាមដៃទេ។"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះមិនមានឧបករណ៍ចាប់ស្នាមម្រាមដៃទេ។"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទឧបករណ៍ចាប់សញ្ញាជាបណ្តោះអាសន្ន។"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"មិនអាចប្រើឧបករណ៍ចាប់ស្នាមម្រាមដៃបានទេ។ សូមទាក់ទងក្រុមហ៊ុនផ្ដល់ការជួសជុល"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ឧបករណ៍នេះមិនមានឧបករណ៍ចាប់ស្នាមម្រាមដៃទេ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"បានចុចប៊ូតុងថាមពល"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃទី <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index b89a1868067e..db4bf180704a 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆಟಪ್ ಮಾಡುವ ಅವಧಿ ಮುಗಿದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ಬಳಕೆದಾರರು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಸ್ಕ್ರೀನ್ಲಾಕ್ ಬಳಸಿ."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ಈ ಸಾಧನವು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ಪವರ್ ಬಟನ್ ಒತ್ತಲಾಗಿದೆ"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index e7a04242eebe..e654eb2030f0 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"지문 인식 하드웨어를 사용할 수 없습니다."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"지문을 설정할 수 없음"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"지문 설정 시간이 초과되었습니다. 다시 시도해 주세요."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"지문 인식 작업이 취소되었습니다."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"사용자가 지문 인식 작업을 취소했습니다."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"지문을 처리할 수 없습니다. 다시 시도해 주세요."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"지문 센서를 사용할 수 없습니다. 수리업체에 방문하세요."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"기기에 지문 센서가 없습니다."</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"전원 버튼을 눌렀습니다."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 91be9e419b96..39ecc0260d9c 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Жүздүн аныктыгы текшерилди"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Манжа изи жөндөлбөй жатат"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Манжа изин коюу убакыты бүтүп калды. Кайра аракет кылыңыз."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Манжа изи иштетилген жок. Кайра аракет кылыңыз."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Манжа изинин сенсорун колдонууга болбойт. Тейлөө кызматына кайрылыңыз"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бул түзмөктө манжа изинин сенсору жок"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Кубат баскычы басылды"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 7d49b0522ab6..48d62d614976 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ບໍ່ມີຮາດແວລາຍນີ້ວມືໃຫ້ຢູ່."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ບໍ່ສາມາດຕັ້ງຄ່າລາຍນິ້ວມືໄດ້"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ໝົດເວລາຕັ້ງຄ່າລາຍນິ້ວມື. ກະລຸນາລອງໃໝ່."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ຍົກເລີກການດຳເນີນການລາຍນີ້ວມືແລ້ວ."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ບໍ່ສາມາດປະມວນຜົນລາຍນິ້ວມືໄດ້. ກະລຸນາລອງໃໝ່."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ບໍ່ສາມາດໃຊ້ເຊັນເຊີລາຍນິ້ວມືໄດ້. ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ກົດປຸ່ມເປີດປິດແລ້ວ"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວມື <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index bf750b9cbed7..38aa52a17b3e 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Veidas autentifikuotas"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Piršto antspaudo aparatinė įranga nepasiekiama."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nepavyko nustatyti kontrolinio kodo"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Baigėsi piršto atspaudo sąrankos skirtasis laikas. Bandykite dar kartą."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Piršto antspaudo operacija atšaukta."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Piršto antspaudo operaciją atšaukė naudotojas."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Per daug bandymų. Naudokite ekrano užraktą."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Per daug bandymų. Naudokite ekrano užraktą."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Negalima naudoti kontrolinio kodo jutiklio. Apsilankykite pas taisymo paslaugos teikėją"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šiame įrenginyje nėra piršto antspaudo jutiklio"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Paspaustas maitinimo mygtukas"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index b554040891c2..165eccde23a4 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Seja autentificēta"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Nospieduma aparatūra nav pieejama."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nevar iestatīt pirksta nospiedumu"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Iestatot pirksta nospiedumu, iestājās noildze. Mēģiniet vēlreiz."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Nospieduma darbība neizdevās."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Lietotājs atcēla pirksta nospieduma darbību."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nevar izmantot pirksta nospieduma sensoru. Sazinieties ar remonta pakalpojumu sniedzēju."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šajā ierīcē nav pirksta nospieduma sensora."</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tika nospiesta barošanas poga"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index ad2274142ee3..aa5645a30d4a 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е проверено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардверот за отпечатоци не е достапен."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се постави отпечаток"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Времето за поставување отпечаток истече. Обидете се повторно."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцијата со отпечаток се откажа."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисникот ја откажа потврдата со отпечаток."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Премногу обиди. Користете заклучување екран."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Премногу обиди. Користете заклучување екран."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не може да се обработи отпечатокот од прст. Обидете се повторно."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не може да се користи сензорот за отпечатоци. Однесете го на поправка"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Уредов нема сензор за отпечатоци"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснато е копчето за вклучување"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index b91c8cfd1f24..0ef12814e3c9 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ ലഭ്യമല്ല."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കാനാകില്ല"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ഫിംഗർപ്രിന്റ് സജ്ജീകരണം ടൈംഔട്ടായി. വീണ്ടും ശ്രമിക്കുക."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്റ് പ്രവർത്തനം."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനാകില്ല. വീണ്ടും ശ്രമിക്കുക."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"വിരലടയാള സെൻസർ ഉപയോഗിക്കാനാകുന്നില്ല. റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസർ ഇല്ല"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"പവർ ബട്ടൺ അമർത്തി"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ഫിംഗർ <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index e17684a6ff0a..df4148ef296f 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Хурууны хээг нотолсон"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Царайг баталгаажууллаа"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хурууны хээний төхөөрөмж бэлэн бус байна."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Хурууны хээ тохируулах боломжгүй"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Хурууны хээний тохируулга завсарласан. Дахин оролдоно уу."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Хурууны хээг боловсруулах боломжгүй. Дахин оролдоно уу."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Хурууны хээ мэдрэгч ашиглах боломжгүй. Засварын үйлчилгээ үзүүлэгчид зочилно уу"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Асаах/Унтраах товчийг дарсан"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index ca26b71b3b2e..09c2ac3b2c48 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरा ऑथेंटिकेशन केलेला आहे"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म प्रेस करा"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फिंगरप्रिंट हार्डवेअर उपलब्ध नाही."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिंट सेट करता आली नाही"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिंट सेट करण्याची वेळ संपली आहे. पुन्हा प्रयत्न करा."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिंटवर प्रक्रिया करू शकत नाही. पुन्हा प्रयत्न करा."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फिंगरप्रिंट सेन्सर वापरू शकत नाही. दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"या डिव्हाइसवर फिंगरप्रिंट सेन्सर नाही"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पॉवर बटण दाबले"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिंट वापरा"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 5865a7493b47..5018aa8fddc7 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah disahkan"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Perkakasan cap jari tidak tersedia."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyediakan cap jari"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Persediaan cap jari telah tamat masa. Cuba lagi."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Pengendalian cap jari dibatalkan."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Pengendalian cap jari dibatalkan oleh pengguna."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak percubaan. Gunakan kunci skrin."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak percubaan. Gunakan kunci skrin."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses cap jari. Cuba lagi."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tidak boleh menggunakan penderia cap jari. Lawati penyedia pembaikan"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Peranti ini tiada penderia cap jari"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butang kuasa ditekan"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index c77c8aac3b12..97121dd39f74 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"လက်ဗွေ စက်ပစ္စည်းမရနိုင်ပါ။"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"လက်ဗွေကို စနစ်ထည့်သွင်း၍ မရပါ"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"လက်ဗွေစနစ်ထည့်သွင်းချိန် ကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"လက်ဗွေကို လုပ်ဆောင်နိုင်ခြင်းမရှိပါ။ ထပ်စမ်းကြည့်ပါ။"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"လက်ဗွေ အာရုံခံကိရိယာကို အသုံးပြု၍ မရပါ။ ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ဤစက်ပစ္စည်းတွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ဖွင့်ပိတ်ခလုတ် နှိပ်ထားသည်"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 2bed82e71808..ca6df0e02195 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet er autentisert"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maskinvare for fingeravtrykk er ikke tilgjengelig."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan ikke konfigurere fingeravtrykk"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigureringen av fingeravtrykk er tidsavbrutt. Prøv på nytt."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrykk-operasjonen ble avbrutt."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"For mange forsøk. Bruk skjermlås i stedet."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"For mange forsøk. Bruk skjermlås i stedet."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan ikke behandle fingeravtrykket. Prøv på nytt."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan ikke bruke fingeravtrykkssensoren. Gå til en reparasjonsleverandør"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enheten har ikke fingeravtrykkssensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen ble trykket"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 6e4c0d200821..fd31ac4b6dbe 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिन्ट सेटअप गर्न सकिएन"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिन्ट सेट अप गर्ने समय सकियो। फेरि प्रयास गर्नुहोस्।"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिन्ट पहिचान गर्ने प्रक्रिया अघि बढाउन सकिएन। फेरि प्रयास गर्नुहोस्।"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फिंगरप्रिन्ट सेन्सर प्रयोग गर्न मिल्दैन। फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"यो डिभाइसमा कुनै फिंगरप्रिन्ट सेन्सर छैन"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन थिचियो"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 2de4cb9acf39..e68947e8ed42 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gezicht geverifieerd"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware voor vingerafdruk niet beschikbaar."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan vingerafdruk niet instellen"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Time-out bij instellen van vingerafdruk. Probeer het opnieuw."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukbewerking geannuleerd."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukverificatie geannuleerd door gebruiker."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan vingerafdruksensor niet gebruiken. Ga naar een reparateur."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dit apparaat heeft geen vingerafdruksensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/uit-knop ingedrukt"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 2a4a07ef9746..45b49d373588 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ଟିପଚିହ୍ନକୁ ସେଟ୍ ଅପ୍ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ସେଟଅପର ସମୟସୀମା ସମାପ୍ତ ହୋଇଯାଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ଟିପଚିହ୍ନକୁ ପ୍ରକ୍ରିୟାନ୍ୱିତ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍ରେ ଟିପଚିହ୍ନ ସେନ୍ସର୍ ନାହିଁ।"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ଏହି ଡିଭାଇସ୍ରେ ଟିପଚିହ୍ନ ସେନସର୍ ନାହିଁ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ପାୱାର ବଟନ ଦବାଯାଇଛି"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index db4c38e7349c..2abe228d6733 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਮੁਰੰਮਤ ਪ੍ਰਦਾਨਕ \'ਤੇ ਜਾਓ"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"\'ਪਾਵਰ\' ਬਟਨ ਦਬਾਇਆ ਗਿਆ"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 1dcc7393f0a9..06bff2aaad55 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Twarz rozpoznana"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Czytnik linii papilarnych nie jest dostępny."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nie można skonfigurować odcisku palca"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Upłynął limit czasu konfiguracji odcisku palca. Spróbuj ponownie."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Odczyt odcisku palca został anulowany."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Odczyt odcisku palca został anulowany przez użytkownika."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zbyt wiele prób. Użyj blokady ekranu."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zbyt wiele prób. Użyj blokady ekranu."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nie można użyć czytnika linii papilarnych. Odwiedź serwis."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"To urządzenie nie jest wyposażone w czytnik linii papilarnych"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Naciśnięto przycisk zasilania"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index b0a3adbed054..43cdfa205162 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 735aa7c8f508..74df1872a970 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado."</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não é possível configurar a impressão digital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente novamente."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo utilizador."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não é possível processar a impressão digital. Tente novamente."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem sensor de impressões digitais."</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão ligar/desligar premido"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar a impressão digital"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index b0a3adbed054..43cdfa205162 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 820ca895258a..67ade37b2756 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware-ul pentru amprentă nu este disponibil."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nu se poate configura amprenta"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Configurarea amprentei a expirat. Încearcă din nou."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operațiunea privind amprenta a fost anulată."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operațiunea privind amprenta a fost anulată de utilizator."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Prea multe încercări. Folosește blocarea ecranului."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Prea multe încercări. Folosește blocarea ecranului."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nu putem procesa amprenta. Încearcă din nou."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nu se poate folosi senzorul de amprentă. Vizitează un furnizor de servicii de reparații."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Acest dispozitiv nu are senzor de amprentă"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"A fost apăsat butonul de pornire"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosește amprenta"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 0c80e0ec709e..7e8df9ff23ff 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер недоступен"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не удалось сохранить отпечаток."</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Время настройки отпечатка пальца истекло. Повторите попытку."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операция с отпечатком отменена."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операция с отпечатком пальца отменена пользователем."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не удалось распознать отпечаток пальца. Повторите попытку."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Невозможно использовать сканер отпечатков пальцев. Обратитесь в сервисный центр."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На этом устройстве нет сканера отпечатков пальцев"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Нажата кнопка питания."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 0aa3200545cb..db7fb6d13b94 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"මුහුණ සත්යාපනය කරන ලදී"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ඇඟිලි සලකුණ පිහිටුවිය නොහැකිය"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ඇඟිලි සලකුණු පිහිටුවීම කාලය නිමා විය. නැවත උත්සාහ කරන්න."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදී."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ඇඟිලි සලකුණ සැකසීමට නොහැක. නැවත උත්සාහ කරන්න."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ඇඟිලි සලකුණු සංවේදකය භාවිත කළ නොහැකිය. අළුත්වැඩියා සැපයුම්කරුවෙකු බලන්න"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"බල බොත්තම ඔබා ඇත"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ඇඟිලි සලකුණ භාවිත කරන්න"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 2ddfbffe4b7c..e199235ac19a 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Tvár bola overená"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Odtlačok prsta sa nedá nastaviť"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nastavenie odtlačku prsta vypršalo. Skúste to znova."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Odtlačok prsta sa nedá spracovať. Skúste to znova."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Senzor odtlačkov prstov nie je možné používať. Navštívte poskytovateľa opráv."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zariadenie nemá senzor odtlačkov prstov"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bol stlačený vypínač"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 191570374251..305e3903397d 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Pristnost obraza je potrjena"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Strojna oprema za prstne odtise ni na voljo."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Prstnega odtisa ni mogoče nastaviti."</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Čas za nastavitev prstnega odtisa je potekel. Poskusite znova."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Dejanje s prstnim odtisom je bilo preklicano."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Dejanje s prstnim odtisom je preklical uporabnik."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Preveč poskusov. Odklenite z načinom za zaklepanje zaslona."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Preveč poskusov. Odklenite z zaklepanjem zaslona."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Prstnega odtisa ni mogoče obdelati. Poskusite znova."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tipala prstnih odtisov ni mogoče uporabiti. Obiščite ponudnika popravil."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ta naprava nima tipala prstnih odtisov"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Gumb za vklop je pritisnjen."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 9b3935173de1..c6fdb8f9440c 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Fytyra u vërtetua"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardueri i gjurmës së gishtit nuk mundësohet."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nuk mund të konfigurohet gjurma e gishtit"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurimi i gjurmës së gishtit skadoi. Provo përsëri."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operacioni i gjurmës së gishtit u anulua."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Gjurma e gishtit nuk mund të përpunohet. Provo përsëri."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sensori i gjurmës së gishtit nuk mund të përdoret. Vizito një ofrues të shërbimit të riparimit"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kjo pajisje nuk ka sensor të gjurmës së gishtit"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butoni i energjisë u shtyp"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 8440b939466e..194e48957a02 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -667,18 +667,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардвер за отиске прстију није доступан."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Подешавање отиска прста није успело"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Време за подешавање отиска прста је истекло. Пробајте поново."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Радња са отиском прста је отказана."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисник је отказао радњу са отиском прста."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Превише покушаја. Користите закључавање екрана уместо тога."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Превише покушаја. Користите закључавање екрана уместо тога."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Обрађивање отиска прста није успело. Пробајте поново."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не можете да користите сензор за отисак прста. Посетите добављача за поправке"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Овај уређај нема сензор за отисак прста"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснуто је дугме за укључивање"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index cfd3aefbd932..25109b80a77a 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Det finns ingen maskinvara för fingeravtryck."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiden för fingeravtrycksinställning gick ut. Försök igen."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"För många försök. Använd låsskärmen i stället."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Det går inte att använda fingeravtryckssensorn. Besök ett reparationsställe"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Enheten har ingen fingeravtryckssensor"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen nedtryckt"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index ff1a603c1d89..8819f2707593 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Uso umethibitishwa"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maunzi ya alama ya kidole hayapatikani."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Imeshindwa kuweka mipangilio ya alama ya kidole"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Muda wa kuweka alama ya kidole umeisha. Jaribu tena."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Mchakato wa alama ya kidole umeghairiwa."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Imeshindwa kutambua alama ya kidole. Jaribu tena."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Imeshindwa kutumia kitambua alama ya kidole. Tembelea mtoa huduma za urekebishaji"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kifaa hiki hakina kitambua alama ya kidole"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Kitufe cha kuwasha au kuzima kimebonyezwa"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 6074fdf606e1..24307fa95389 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"கைரேகை அங்கீகரிக்கப்பட்டது"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"முகம் அங்கீகரிக்கப்பட்டது"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"கைரேகை வன்பொருள் இல்லை."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"கைரேகையை அமைக்க முடியவில்லை"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"கைரேகை அமைவுக்கான நேரம் முடிந்துவிட்டது. மீண்டும் முயலவும்."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"கைரேகை சென்சாரைப் பயன்படுத்த முடியவில்லை. பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"பவர் பட்டன் அழுத்தப்பட்டுள்ளது"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 3f729667ad04..28b7d2804681 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"వేలిముద్ర ప్రమాణీకరించబడింది"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ముఖం ప్రమాణీకరించబడింది"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"వేలిముద్ర హార్డ్వేర్ అందుబాటులో లేదు."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"వేలిముద్ర సెటప్ సమయం ముగిసింది. మళ్లీ ట్రై చేయండి."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్ను ఉపయోగించండి."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్ను ఉపయోగించండి."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"వేలిముద్ర సెన్సార్ను ఉపయోగించడం సాధ్యం కాదు. రిపెయిర్ ప్రొవైడర్ను సందర్శించండి"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ లేదు"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>వ వేలు"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 2f8486ce2763..94fe59d18549 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -514,8 +514,8 @@ <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"แอปนี้ถ่ายภาพและวิดีโอด้วยกล้องได้ทุกเมื่อ"</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องของระบบเพื่อถ่ายภาพและวิดีโอ"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"แอปของระบบหรือที่ได้รับสิทธิ์นี้จะถ่ายภาพและบันทึกวิดีโอโดยใช้กล้องของระบบได้ทุกเมื่อ แอปต้องมีสิทธิ์ android.permission.CAMERA ด้วย"</string> - <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับโค้ดเรียกกลับเมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string> - <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"แอปนี้จะได้รับโค้ดเรียกกลับเมื่อมีการปิดหรือเปิดอุปกรณ์กล้อง (โดยแอปพลิเคชันที่เปิด)"</string> + <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับ Callback เมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string> + <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"แอปนี้จะได้รับ Callback เมื่อมีการปิดหรือเปิดอุปกรณ์กล้อง (โดยแอปพลิเคชันที่เปิด)"</string> <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string> <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"แอปนี้เข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"ควบคุมการสั่นเตือน"</string> @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"ตั้งค่าลายนิ้วมือไม่ได้"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"การตั้งค่าลายนิ้วมือหมดเวลา โปรดลองอีกครั้ง"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"ยกเลิกการทำงานของลายนิ้วมือ"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้ โปรดติดต่อผู้ให้บริการซ่อม"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"กดปุ่มเปิด/ปิดแล้ว"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้วมือ <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 3cb5babd5fcb..57c6b2ab3b46 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Na-authenticate ang mukha"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hindi available ang hardware na ginagamitan ng fingerprint."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Hindi ma-set up ang fingerprint"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nag-time out ang pag-set up ng fingerprint. Subukan ulit."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Nakansela ang operasyong ginagamitan ng fingerprint."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kinansela ng user ang operasyon sa fingerprint."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Hindi maproseso ang fingerprint. Subukan ulit."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Hindi magamit ang sensor para sa fingerprint. Bumisita sa provider ng pag-aayos"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Walang sensor para sa fingerprint ang device na ito"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Napindot ang power button"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 48beb9a0a8d7..af18dacc05c2 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yüz kimliği doğrulandı"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Parmak izi donanımı kullanılamıyor."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Parmak izi ayarlanamıyor"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Parmak izi kurulumu zaman aşımına uğradı. Tekrar deneyin."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Parmak izi işlemi iptal edildi."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Parmak izi işlenemiyor. Tekrar deneyin."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Parmak izi sensörü kullanılamıyor. Bir onarım hizmeti sağlayıcıyı ziyaret edin"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda parmak izi sensörü yok"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Güç düğmesine basıldı"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 08051a76a1a6..5b645db69daa 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -668,18 +668,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Обличчя автентифіковано"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер відбитків пальців недоступний."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не вдалося створити відбиток пальця"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Час очікування для налаштування відбитка пальця минув. Повторіть спробу."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Забагато спроб. Використайте натомість розблокування екрана."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не вдається скористатися сканером відбитків пальців. Зверніться до постачальника послуг із ремонту."</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На цьому пристрої немає сканера відбитків пальців"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Натиснуто кнопку живлення"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Доступ за відбитком пальця"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index ba8dfe376caf..1c9275293cca 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"فنگر پرنٹ کی تصدیق ہو گئی"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چہرے کی تصدیق ہو گئی"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"فنگر پرنٹ کو سیٹ اپ نہیں کیا جا سکا"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"فنگر پرنٹ کے سیٹ اپ کا وقت ختم ہو گیا۔ دوبارہ کوشش کریں۔"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"فنگر پرنٹ پروسیس نہیں ہو سکتا۔ دوبارہ کوشش کریں۔"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"فنگر پرنٹ سینسر کا استعمال نہیں کر سکتے۔ ایک مرمت فراہم کنندہ کو ملاحظہ کریں"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"پاور بٹن دبایا گیا"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index b4c6a8d87735..828f391345f0 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yuzingiz aniqlandi"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmoq izi skaneri ish holatida emas."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmoq izi sozlanmadi"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmoq izini sozlash vaqti tugadi. Qayta urining."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmoq izi tekshiruvi bekor qilindi."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmoq izi tekshirilmadi. Qayta urining."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Barmoq izi skaneridan foydalanish imkonsiz. Xizmat koʻrsatish markaziga murojaat qiling"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu qurilmada barmoq izi skaneri yo‘q"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Quvvat tugmasi bosildi"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 61f134800c41..2e57cafaec1f 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Đã xác thực khuôn mặt"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Phần cứng vân tay không khả dụng."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Không thể thiết lập vân tay"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hết thời gian chờ thiết lập vân tay. Hãy thử lại."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Thao tác dùng dấu vân tay bị hủy."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Người dùng đã hủy thao tác dùng dấu vân tay."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Không xử lý được vân tay. Hãy thử lại."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Không thể dùng cảm biến vân tay. Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Thiết bị này không có cảm biến vân tay"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Đã nhấn nút nguồn"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 09e80eb8f7c0..0d49373867fb 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已验证"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指纹硬件无法使用。"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"无法设置指纹"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指纹设置已超时,请重试。"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"指纹操作已取消。"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"用户取消了指纹操作。"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"尝试次数过多,请通过屏幕锁定功能解锁。"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"尝试次数过多,请通过屏幕锁定功能解锁。"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"无法处理指纹,请重试。"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"无法使用指纹传感器。请联系维修服务提供商"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此设备没有指纹传感器"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下电源按钮"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 8743f8548a71..cce19905c2ff 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -153,15 +153,15 @@ <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string> <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string> <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string> - <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"行動網路安全性"</string> - <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"查看設定"</string> - <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"已存取裝置 ID"</string> - <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> 連線上的網路已記錄裝置的專屬 ID (IMSI),次數為 <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> 次;記錄區間為 <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g>至 <xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g>。"</string> - <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"已連上加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string> - <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"你現在已連上較安全的行動網路。"</string> - <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"已連上未加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string> - <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"你已連上未加密的行動網路。通話、訊息和資料會容易遭到攔截。"</string> - <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"行動網路安全性設定"</string> + <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"流動網絡安全性"</string> + <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"檢查設定"</string> + <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"已存取裝置識別碼"</string> + <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> 連線上的網絡已記錄裝置的專屬識別碼 (IMSI),次數為 <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> 次;記錄區間為 <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g>至<xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g>。"</string> + <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"已連線至加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string> + <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"你現已連線至較安全的流動網絡。"</string> + <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"已連線至未加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string> + <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"你已連線至未加密的流動網絡。通話、訊息和資料會容易被攔截。"</string> + <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"流動網絡安全性設定"</string> <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"瞭解詳情"</string> <string name="fcComplete" msgid="1080909484660507044">"功能碼輸入完成。"</string> <string name="fcError" msgid="5325116502080221346">"連線問題或功能碼無效。"</string> @@ -553,8 +553,8 @@ <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"允許應用程式使用手機的紅外線傳送器。"</string> <string name="permlab_setWallpaper" msgid="6959514622698794511">"設定桌布"</string> <string name="permdesc_setWallpaper" msgid="2973996714129021397">"允許應用程式設定系統桌布。"</string> - <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"存取隱藏的設定檔"</string> - <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"允許應用程式存取隱藏的設定檔。"</string> + <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"存取已隱藏的設定檔"</string> + <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"允許應用程式存取已隱藏的設定檔。"</string> <string name="permlab_setWallpaperHints" msgid="1153485176642032714">"調整桌布大小"</string> <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"允許應用程式設定有關系統桌布大小的提示。"</string> <string name="permlab_setTimeZone" msgid="7922618798611542432">"設定時區"</string> @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已經驗證"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"無法使用指紋軟件。"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋操作已取消。"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋操作。"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"無法使用指紋感應器。請諮詢維修服務供應商"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此裝置沒有指紋感應器"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下開關按鈕"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string> @@ -823,8 +829,8 @@ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以大於 200 Hz 的頻率對感應器資料進行取樣"</string> <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"更新應用程式,無需使用者操作"</string> <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"允許擁有者更新先前安裝的應用程式,無需使用者操作"</string> - <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"更新 E2EE 聯絡人金鑰的驗證狀態,這些金鑰為其他應用程式所有"</string> - <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"允許應用程式更新 E2EE 聯絡人金鑰的驗證狀態,這些金鑰為其他應用程式所有"</string> + <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"更新其他應用程式擁有的點對點加密聯絡人密鑰的驗證狀態"</string> + <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"允許應用程式更新其他應用程式擁有的點對點加密聯絡人密鑰的驗證狀態"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"控制螢幕鎖定密碼和 PIN 所允許的長度和字元。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string> @@ -2386,8 +2392,8 @@ <string name="profile_label_test" msgid="9168641926186071947">"測試"</string> <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> - <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連上衛星"</string> - <string name="satellite_notification_summary" msgid="5207364139430767162">"你可以收發訊息,沒有行動/Wi-Fi 網路也無妨"</string> - <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string> + <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連線至衛星"</string> + <string name="satellite_notification_summary" msgid="5207364139430767162">"你可在沒有流動/Wi-Fi 網絡的情況下收發訊息"</string> + <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string> <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index ba257da04279..ff31b74fbea2 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"臉孔驗證成功"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋硬體無法使用。"</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋作業已取消。"</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋驗證作業。"</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"指紋感應器無法使用,請洽詢維修供應商"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"這個裝置沒有指紋感應器"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下電源鍵"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 04c60cb5c1b8..5112da966e7f 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -666,18 +666,24 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ubuso bufakazelwe ubuqiniso"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali."</string> + <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) --> + <skip /> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ayikwazi ukusetha izigxivizo zeminwe"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Ukusethwa kwesigxivizo somunwe kuphelelwe yisikhathi Zama futhi."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string> + <!-- no translation found for fingerprint_error_canceled (5541771463159727513) --> + <skip /> + <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) --> + <skip /> <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ayikwazi ukucubungula isigxivizo somunwe. Zama futhi."</string> - <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string> - <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string> - <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ayikwazi ukusebenzisa inzwa yesigxivizo somunwe. Vakashela umhlinzeki wokulungisa"</string> + <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) --> + <skip /> + <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Le divayisi ayinayo inzwa yezigxivizo zeminwe"</string> + <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) --> + <skip /> + <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) --> + <skip /> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Inkinobho yamandla icindezelwe"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string> <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 4ee03deab6c2..c1fd61948e68 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2514,6 +2514,13 @@ <!-- The icon is shown unless the launching app specified SPLASH_SCREEN_STYLE_EMPTY --> <enum name="icon_preferred" value="1" /> </attr> + <!-- Offer Window the ability to opt out the UI Toolkit discrete variable refresh rate. + This feature allows device to adjust refresh rate as needed and + can be useful for power saving. + Set to false to reduce the frame rate optimizations on devices with + variable refresh rate screens. + The default is true. --> + <attr name="windowIsFrameRatePowerSavingsBalanced" format="boolean"/> <!-- Flag indicating whether this window would opt-out the edge-to-edge enforcement. @@ -5891,6 +5898,17 @@ use glyph bound's as a source of text width. --> <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> <attr name="useBoundsForWidth" format="boolean" /> + + + <!-- Whether to shift the drawing offset for prevent clipping start drawing offset. + This value is ignored when the useBoundsForWidth attribute is false. + + If this value is false, the TextView draws text from the zero X coordinate. This is + useful for aligning multiple TextViews vertically. + If this value is true, the TextView shift the drawing offset not to clip the + stroke in the region where the X coordinate is negative. --> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <attr name="shiftDrawingOffsetForStartOverhang" format="boolean" /> <!-- Whether to use the locale preferred line height for the minimum line height. This flag is useful for preventing jitter of entering letters into empty EditText. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 647bccfb0c89..b2e0be7c2201 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1919,6 +1919,12 @@ try to load its code when launching components. The default is true for normal behavior. --> <attr name="hasCode" format="boolean" /> + <!-- Specifies if activities can be launched on top of this application by activities from + other applications in the same task. If set to false, activity launches which would + replace this application with another when in the user's view will be blocked. + The default is true. --> + <!-- @FlaggedApi("android.security.asm_restrictions_enabled") --> + <attr name="allowCrossUidActivitySwitchFromBelow" format="boolean" /> <attr name="persistent" /> <attr name="persistentWhenFeatureAvailable" /> <attr name="requiredForAllUsers" /> @@ -3289,7 +3295,8 @@ {@link java.lang.SecurityException}. <p> Note that the enforcement works for content URIs inside - {@link android.content.Intent#getData} and {@link android.content.Intent#getClipData}. + {@link android.content.Intent#getData}, {@link android.content.Intent#EXTRA_STREAM}, + and {@link android.content.Intent#getClipData}. @FlaggedApi("android.security.content_uri_permission_apis") --> <attr name="requireContentUriPermissionFromCaller" format="string"> <!-- Default, no specific permissions are required. --> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index dcb6bb0cd743..c797210345a2 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -159,6 +159,14 @@ <public name="contentSensitivity" /> <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") --> <public name="supportsConnectionlessStylusHandwriting" /> + <!-- @FlaggedApi("android.nfc.Flags.FLAG_OBSERVE_MODE") --> + <public name="defaultToObserveMode"/> + <!-- @FlaggedApi("android.security.asm_restrictions_enabled") --> + <public name="allowCrossUidActivitySwitchFromBelow"/> + <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") --> + <public name="shiftDrawingOffsetForStartOverhang" /> + <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") --> + <public name="windowIsFrameRatePowerSavingsBalanced"/> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/tests/BroadcastRadioTests/TEST_MAPPING b/core/tests/BroadcastRadioTests/TEST_MAPPING index b085a27b25b8..563706374bf2 100644 --- a/core/tests/BroadcastRadioTests/TEST_MAPPING +++ b/core/tests/BroadcastRadioTests/TEST_MAPPING @@ -1,5 +1,5 @@ { - "postsubmit": [ + "presubmit": [ { "name": "BroadcastRadioTests" } diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml index 32eebb35e0c3..78cd1e1e47e8 100644 --- a/core/tests/coretests/res/values/styles.xml +++ b/core/tests/coretests/res/values/styles.xml @@ -55,4 +55,10 @@ <style name="ViewDefaultBackground"> <item name="android:background">#00000000</item> </style> + <style name="IsFrameRatePowerSavingsBalancedDisabled"> + <item name="android:windowIsFrameRatePowerSavingsBalanced">false</item> + </style> + <style name="IsFrameRatePowerSavingsBalancedEnabled"> + <item name="android:windowIsFrameRatePowerSavingsBalanced">true</item> + </style> </resources> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 2327b20bada6..48ef7e6f11a8 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -40,6 +40,7 @@ import android.app.ActivityThread.ActivityClientRecord; import android.app.Application; import android.app.IApplicationThread; import android.app.PictureInPictureParams; +import android.app.PictureInPictureUiState; import android.app.ResourcesManager; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityLifecycleItem; @@ -706,6 +707,9 @@ public class ActivityThreadTest { final TestActivity activity = mActivityTestRule.launchActivity(startIntent); final ActivityThread activityThread = activity.getActivityThread(); final ActivityClientRecord r = getActivityClientRecord(activity); + if (android.app.Flags.enablePipUiStateCallbackOnEntering()) { + activity.mPipUiStateLatch = new CountDownLatch(1); + } InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { activityThread.handlePictureInPictureRequested(r); @@ -940,6 +944,11 @@ public class ActivityThreadTest { * latch reaches 0. */ volatile CountDownLatch mConfigLatch; + /** + * A latch used to notify tests that we're about to wait for the + * onPictureInPictureUiStateChanged callback. + */ + volatile CountDownLatch mPipUiStateLatch; @Override protected void onCreate(Bundle savedInstanceState) { @@ -974,6 +983,14 @@ public class ActivityThreadTest { if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) { enterPictureInPictureMode(new PictureInPictureParams.Builder().build()); mPipEntered = true; + // Await for onPictureInPictureUiStateChanged callback if applicable + if (mPipUiStateLatch != null) { + try { + mPipUiStateLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } return true; } else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) { mPipEnterSkipped = true; @@ -982,6 +999,13 @@ public class ActivityThreadTest { return super.onPictureInPictureRequested(); } + @Override + public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { + if (mPipUiStateLatch != null && pipState.isEnteringPip()) { + mPipUiStateLatch.countDown(); + } + } + boolean pipRequested() { return mPipRequested; } diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java index 661b210f3e18..402d08eb4a25 100644 --- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java +++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java @@ -27,6 +27,8 @@ import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyResourcesManager; @@ -118,12 +120,55 @@ public class CrossProfileAppsTest { public void initUsers() throws Exception { when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false); when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true); + when(mUserManager.isProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false); + when(mUserManager.isProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true); mTargetProfiles = new ArrayList<>(); when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles); } @Test + public void isProfile_managedProfile_returnsTrue() { + setValidTargetProfile(MANAGED_PROFILE); + + boolean result = mCrossProfileApps.isProfile(MANAGED_PROFILE); + + assertTrue(result); + } + + @Test + public void isProfile_personalProfile_returnsFalse() { + setValidTargetProfile(PERSONAL_PROFILE); + + boolean result = mCrossProfileApps.isProfile(PERSONAL_PROFILE); + + assertFalse(result); + } + + @Test + public void isManagedProfile_managedProfile_returnsTrue() { + setValidTargetProfile(MANAGED_PROFILE); + + boolean result = mCrossProfileApps.isManagedProfile(MANAGED_PROFILE); + + assertTrue(result); + } + + @Test + public void isManagedProfile_personalProfile_returnsFalse() { + setValidTargetProfile(PERSONAL_PROFILE); + + boolean result = mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE); + + assertFalse(result); + } + + @Test(expected = SecurityException.class) + public void isManagedProfile_notValidTarget_throwsSecurityException() { + mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE); + } + + @Test public void getProfileSwitchingLabel_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); when(mApplicationInfo.loadSafeLabel(any(), anyFloat(), anyInt())).thenReturn("app"); diff --git a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java index aa89e04b4b7e..cc342cf0fc80 100644 --- a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java +++ b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java @@ -17,6 +17,7 @@ package android.os; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import androidx.test.runner.AndroidJUnit4; @@ -24,6 +25,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; @@ -59,10 +62,17 @@ public class RemoteCallbackListTest { mList.register(mRed.mInterface); mList.register(mGreen.mInterface, mCookie); assertEquals(2, mList.getRegisteredCallbackCount()); - assertEquals(mRed.mInterface, mList.getRegisteredCallbackItem(0)); - assertEquals(null, mList.getRegisteredCallbackCookie(0)); - assertEquals(mGreen.mInterface, mList.getRegisteredCallbackItem(1)); - assertEquals(mCookie, mList.getRegisteredCallbackCookie(1)); + + final List<IRemoteCallback> list = new ArrayList<>(); + for (int i = 0; i < mList.getRegisteredCallbackCount(); i++) { + list.add(mList.getRegisteredCallbackItem(i)); + } + final int redIndex = list.indexOf(mRed.mInterface); + final int greenIndex = list.indexOf(mGreen.mInterface); + assertTrue(redIndex >= 0); + assertTrue(greenIndex >= 0); + assertEquals(null, mList.getRegisteredCallbackCookie(redIndex)); + assertEquals(mCookie, mList.getRegisteredCallbackCookie(greenIndex)); mList.unregister(mRed.mInterface); assertEquals(1, mList.getRegisteredCallbackCount()); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 0657e4b9c619..038c00e3823c 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -660,10 +660,22 @@ public class ViewRootImplTest { public void votePreferredFrameRate_voteFrameRateCategory_aggregate() { View view = new View(sContext); attachViewToWindow(view); + ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { - ViewRootImpl viewRootImpl = view.getViewRootImpl(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); + }); + + // reset the frame rate category counts + for (int i = 0; i < 5; i++) { + sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + view.invalidate(); + }); + sInstrumentation.waitForIdleSync(); + } + + sInstrumentation.runOnMainSync(() -> { viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW); viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL); @@ -699,6 +711,8 @@ public class ViewRootImplTest { viewRootImpl.votePreferredFrameRate(24); assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1); viewRootImpl.votePreferredFrameRate(30); + assertEquals(viewRootImpl.getPreferredFrameRate(), 30, 0.1); + viewRootImpl.votePreferredFrameRate(60); assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1); viewRootImpl.votePreferredFrameRate(120); assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1); @@ -721,6 +735,18 @@ public class ViewRootImplTest { sInstrumentation.runOnMainSync(() -> { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); + }); + + // reset the frame rate category counts + for (int i = 0; i < 5; i++) { + sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + view.invalidate(); + }); + sInstrumentation.waitForIdleSync(); + } + + sInstrumentation.runOnMainSync(() -> { view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW); @@ -847,7 +873,18 @@ public class ViewRootImplTest { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); assertEquals(viewRootImpl.getPreferredFrameRate(), frameRate, 0.1); + }); + + // reset the frame rate category counts + for (int i = 0; i < 5; i++) { + sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + view.invalidate(); + }); + sInstrumentation.waitForIdleSync(); + } + sInstrumentation.runOnMainSync(() -> { view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW); @@ -882,33 +919,56 @@ public class ViewRootImplTest { ViewRootImpl viewRootImpl = view.getViewRootImpl(); - // Frequent update + // In transistion from frequent update to infrequent update + Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - assertEquals(viewRootImpl.getPreferredFrameRateCategory(), - FRAME_RATE_CATEGORY_NO_PREFERENCE); - view.invalidate(); - assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); - view.invalidate(); - assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); + // reset the frame rate category counts + for (int i = 0; i < 5; i++) { + sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + view.invalidate(); + }); + sInstrumentation.waitForIdleSync(); + } + // In transistion from frequent update to infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); view.invalidate(); - assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); + assertEquals(viewRootImpl.getPreferredFrameRateCategory(), + FRAME_RATE_CATEGORY_NO_PREFERENCE); }); // Infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); } + /** + * Test the IsFrameRatePowerSavingsBalanced values are properly set + */ + @UiThreadTest + @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() { + ViewRootImpl viewRootImpl = new ViewRootImpl(sContext, + sContext.getDisplayNoVerify()); + assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true); + viewRootImpl.setFrameRatePowerSavingsBalanced(false); + assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), false); + viewRootImpl.setFrameRatePowerSavingsBalanced(true); + assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true); + } + @Test public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java index 3df3b9d2c555..b9841ff997e7 100644 --- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java @@ -21,17 +21,28 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import android.app.Instrumentation; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Binder; 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.view.ActionMode; import android.view.ContextThemeWrapper; +import android.view.ViewRootImpl; +import android.view.WindowManager; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -40,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -54,6 +66,12 @@ public final class PhoneWindowTest { private PhoneWindow mPhoneWindow; private Context mContext; + private static Instrumentation sInstrumentation = + InstrumentationRegistry.getInstrumentation(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getContext(); @@ -154,6 +172,48 @@ public final class PhoneWindowTest { assertThat(colorDrawable.getColor(), is(Color.BLUE)); } + @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void testWindowFrameRateHint_disabled() { + createPhoneWindowWithTheme(R.style.IsFrameRatePowerSavingsBalancedDisabled); + installDecor(); + + DecorView decorView = (DecorView) mPhoneWindow.getDecorView(); + + WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check + + sInstrumentation.runOnMainSync(() -> { + WindowManager wm = mContext.getSystemService(WindowManager.class); + wm.addView(decorView, wmlp); + }); + sInstrumentation.waitForIdleSync(); + + ViewRootImpl viewRootImpl = decorView.getViewRootImpl(); + assertFalse(viewRootImpl.isFrameRatePowerSavingsBalanced()); + } + + @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void testWindowFrameRateHint_enabled() { + createPhoneWindowWithTheme(R.style.IsFrameRatePowerSavingsBalancedEnabled); + installDecor(); + + DecorView decorView = (DecorView) mPhoneWindow.getDecorView(); + + WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check + + sInstrumentation.runOnMainSync(() -> { + WindowManager wm = mContext.getSystemService(WindowManager.class); + wm.addView(decorView, wmlp); + }); + sInstrumentation.waitForIdleSync(); + + ViewRootImpl viewRootImpl = decorView.getViewRootImpl(); + assertTrue(viewRootImpl.isFrameRatePowerSavingsBalanced()); + } + private void createPhoneWindowWithTheme(int theme) { mPhoneWindow = new PhoneWindow(new ContextThemeWrapper(mContext, theme)); } diff --git a/data/etc/com.android.intentresolver.xml b/data/etc/com.android.intentresolver.xml index af6492609157..afd4f7cdba53 100644 --- a/data/etc/com.android.intentresolver.xml +++ b/data/etc/com.android.intentresolver.xml @@ -17,8 +17,10 @@ <permissions> <privapp-permissions package="com.android.intentresolver"> <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.LOG_COMPAT_CHANGE"/> <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> <permission name="android.permission.QUERY_CLONED_APPS"/> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index c8cbb9800626..42e3387e3eb5 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2077,6 +2077,12 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/LockTaskController.java" }, + "-315778658": { + "message": "transferTouchGesture failed because args transferFromToken or transferToToken is null", + "level": "ERROR", + "group": "WM_DEBUG_EMBEDDED_WINDOWS", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-312353598": { "message": "Executing finish of activity: %s", "level": "VERBOSE", @@ -2293,12 +2299,6 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowState.java" }, - "-90559682": { - "message": "Config is skipping already pausing %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-87705714": { "message": "findFocusedWindow: focusedApp=null using new focus @ %s", "level": "VERBOSE", @@ -3547,12 +3547,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1011462000": { - "message": "Re-launching after pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, "1015746067": { "message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index b33a5d2dcef8..f3bb21719890 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1128,6 +1128,30 @@ public class Canvas extends BaseCanvas { return false; } + /** + * Intersect the current clip with the specified shader. + * The shader will be treated as an alpha mask, taking the intersection of the two. + * + * @param shader The shader to intersect with the current clip + */ + @FlaggedApi(Flags.FLAG_CLIP_SHADER) + public void clipShader(@NonNull Shader shader) { + nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(), + Region.Op.INTERSECT.nativeInt); + } + + /** + * Set the clip to the difference of the current clip and the shader. + * The shader will be treated as an alpha mask, taking the difference of the two. + * + * @param shader The shader to intersect with the current clip + */ + @FlaggedApi(Flags.FLAG_CLIP_SHADER) + public void clipOutShader(@NonNull Shader shader) { + nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(), + Region.Op.DIFFERENCE.nativeInt); + } + public @Nullable DrawFilter getDrawFilter() { return mDrawFilter; } @@ -1472,6 +1496,8 @@ public class Canvas extends BaseCanvas { @CriticalNative private static native boolean nClipPath(long nativeCanvas, long nativePath, int regionOp); @CriticalNative + private static native void nClipShader(long nativeCanvas, long nativeShader, int regionOp); + @CriticalNative private static native void nSetDrawFilter(long nativeCanvas, long nativeFilter); @CriticalNative private static native void nGetMatrix(long nativeCanvas, long nativeMatrix); diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java index 3cd709ea10e5..69e19824da26 100644 --- a/graphics/java/android/graphics/pdf/PdfEditor.java +++ b/graphics/java/android/graphics/pdf/PdfEditor.java @@ -25,7 +25,9 @@ import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; + import dalvik.system.CloseGuard; + import libcore.io.IoUtils; import java.io.IOException; @@ -37,6 +39,12 @@ import java.io.IOException; */ public final class PdfEditor { + /** + * Any call the native pdfium code has to be single threaded as the library does not support + * parallel use. + */ + private static final Object sPdfiumLock = new Object(); + private final CloseGuard mCloseGuard = CloseGuard.get(); private long mNativeDocument; @@ -79,7 +87,7 @@ public final class PdfEditor { } mInput = input; - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { mNativeDocument = nativeOpen(mInput.getFd(), size); try { mPageCount = nativeGetPageCount(mNativeDocument); @@ -112,7 +120,7 @@ public final class PdfEditor { throwIfClosed(); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { mPageCount = nativeRemovePage(mNativeDocument, pageIndex); } } @@ -138,12 +146,12 @@ public final class PdfEditor { Point size = new Point(); getPageSize(pageIndex, size); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(), 0, 0, size.x, size.y); } } else { - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(), clip.left, clip.top, clip.right, clip.bottom); } @@ -161,7 +169,7 @@ public final class PdfEditor { throwIfOutSizeNull(outSize); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeGetPageSize(mNativeDocument, pageIndex, outSize); } } @@ -177,7 +185,7 @@ public final class PdfEditor { throwIfOutMediaBoxNull(outMediaBox); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); } } @@ -193,7 +201,7 @@ public final class PdfEditor { throwIfMediaBoxNull(mediaBox); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); } } @@ -209,7 +217,7 @@ public final class PdfEditor { throwIfOutCropBoxNull(outCropBox); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); } } @@ -225,7 +233,7 @@ public final class PdfEditor { throwIfCropBoxNull(cropBox); throwIfPageNotInDocument(pageIndex); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); } } @@ -238,7 +246,7 @@ public final class PdfEditor { public boolean shouldScaleForPrinting() { throwIfClosed(); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { return nativeScaleForPrinting(mNativeDocument); } } @@ -255,7 +263,7 @@ public final class PdfEditor { try { throwIfClosed(); - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeWrite(mNativeDocument, output.getFd()); } } finally { @@ -287,7 +295,7 @@ public final class PdfEditor { private void doClose() { if (mNativeDocument != 0) { - synchronized (PdfRenderer.sPdfiumLock) { + synchronized (sPdfiumLock) { nativeClose(mNativeDocument); } mNativeDocument = 0; diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java deleted file mode 100644 index 4666963b5dd4..000000000000 --- a/graphics/java/android/graphics/pdf/PdfRenderer.java +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright (C) 2014 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.graphics.pdf; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; - -import com.android.internal.util.Preconditions; - -import dalvik.system.CloseGuard; - -import libcore.io.IoUtils; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * <p> - * This class enables rendering a PDF document. This class is not thread safe. - * </p> - * <p> - * If you want to render a PDF, you create a renderer and for every page you want - * to render, you open the page, render it, and close the page. After you are done - * with rendering, you close the renderer. After the renderer is closed it should not - * be used anymore. Note that the pages are rendered one by one, i.e. you can have - * only a single page opened at any given time. - * </p> - * <p> - * A typical use of the APIs to render a PDF looks like this: - * </p> - * <pre> - * // create a new renderer - * PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor()); - * - * // let us just render all pages - * final int pageCount = renderer.getPageCount(); - * for (int i = 0; i < pageCount; i++) { - * Page page = renderer.openPage(i); - * - * // say we render for showing on the screen - * page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY); - * - * // do stuff with the bitmap - * - * // close the page - * page.close(); - * } - * - * // close the renderer - * renderer.close(); - * </pre> - * - * <h3>Print preview and print output</h3> - * <p> - * If you are using this class to rasterize a PDF for printing or show a print - * preview, it is recommended that you respect the following contract in order - * to provide a consistent user experience when seeing a preview and printing, - * i.e. the user sees a preview that is the same as the printout. - * </p> - * <ul> - * <li> - * Respect the property whether the document would like to be scaled for printing - * as per {@link #shouldScaleForPrinting()}. - * </li> - * <li> - * When scaling a document for printing the aspect ratio should be preserved. - * </li> - * <li> - * Do not inset the content with any margins from the {@link android.print.PrintAttributes} - * as the application is responsible to render it such that the margins are respected. - * </li> - * <li> - * If document page size is greater than the printed media size the content should - * be anchored to the upper left corner of the page for left-to-right locales and - * top right corner for right-to-left locales. - * </li> - * </ul> - * - * @see #close() - */ -public final class PdfRenderer implements AutoCloseable { - /** - * Any call the native pdfium code has to be single threaded as the library does not support - * parallel use. - */ - final static Object sPdfiumLock = new Object(); - - private final CloseGuard mCloseGuard = CloseGuard.get(); - - private final Point mTempPoint = new Point(); - - private long mNativeDocument; - - private final int mPageCount; - - private ParcelFileDescriptor mInput; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private Page mCurrentPage; - - /** @hide */ - @IntDef({ - Page.RENDER_MODE_FOR_DISPLAY, - Page.RENDER_MODE_FOR_PRINT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RenderMode {} - - /** - * Creates a new instance. - * <p> - * <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>, - * i.e. its data being randomly accessed, e.g. pointing to a file. - * </p> - * <p> - * <strong>Note:</strong> This class takes ownership of the passed in file descriptor - * and is responsible for closing it when the renderer is closed. - * </p> - * <p> - * If the file is from an untrusted source it is recommended to run the renderer in a separate, - * isolated process with minimal permissions to limit the impact of security exploits. - * </p> - * - * @param input Seekable file descriptor to read from. - * - * @throws java.io.IOException If an error occurs while reading the file. - * @throws java.lang.SecurityException If the file requires a password or - * the security scheme is not supported. - */ - public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException { - if (input == null) { - throw new NullPointerException("input cannot be null"); - } - - final long size; - try { - Os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET); - size = Os.fstat(input.getFileDescriptor()).st_size; - } catch (ErrnoException ee) { - throw new IllegalArgumentException("file descriptor not seekable"); - } - mInput = input; - - synchronized (sPdfiumLock) { - mNativeDocument = nativeCreate(mInput.getFd(), size); - try { - mPageCount = nativeGetPageCount(mNativeDocument); - } catch (Throwable t) { - nativeClose(mNativeDocument); - mNativeDocument = 0; - throw t; - } - } - - mCloseGuard.open("close"); - } - - /** - * Closes this renderer. You should not use this instance - * after this method is called. - */ - public void close() { - throwIfClosed(); - throwIfPageOpened(); - doClose(); - } - - /** - * Gets the number of pages in the document. - * - * @return The page count. - */ - public int getPageCount() { - throwIfClosed(); - return mPageCount; - } - - /** - * Gets whether the document prefers to be scaled for printing. - * You should take this info account if the document is rendered - * for printing and the target media size differs from the page - * size. - * - * @return If to scale the document. - */ - public boolean shouldScaleForPrinting() { - throwIfClosed(); - - synchronized (sPdfiumLock) { - return nativeScaleForPrinting(mNativeDocument); - } - } - - /** - * Opens a page for rendering. - * - * @param index The page index. - * @return A page that can be rendered. - * - * @see android.graphics.pdf.PdfRenderer.Page#close() PdfRenderer.Page.close() - */ - public Page openPage(int index) { - throwIfClosed(); - throwIfPageOpened(); - throwIfPageNotInDocument(index); - mCurrentPage = new Page(index); - return mCurrentPage; - } - - @Override - protected void finalize() throws Throwable { - try { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - - doClose(); - } finally { - super.finalize(); - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private void doClose() { - if (mCurrentPage != null) { - mCurrentPage.close(); - mCurrentPage = null; - } - - if (mNativeDocument != 0) { - synchronized (sPdfiumLock) { - nativeClose(mNativeDocument); - } - mNativeDocument = 0; - } - - if (mInput != null) { - IoUtils.closeQuietly(mInput); - mInput = null; - } - mCloseGuard.close(); - } - - private void throwIfClosed() { - if (mInput == null) { - throw new IllegalStateException("Already closed"); - } - } - - private void throwIfPageOpened() { - if (mCurrentPage != null) { - throw new IllegalStateException("Current page not closed"); - } - } - - private void throwIfPageNotInDocument(int pageIndex) { - if (pageIndex < 0 || pageIndex >= mPageCount) { - throw new IllegalArgumentException("Invalid page index"); - } - } - - /** - * This class represents a PDF document page for rendering. - */ - public final class Page implements AutoCloseable { - - private final CloseGuard mCloseGuard = CloseGuard.get(); - - /** - * Mode to render the content for display on a screen. - */ - public static final int RENDER_MODE_FOR_DISPLAY = 1; - - /** - * Mode to render the content for printing. - */ - public static final int RENDER_MODE_FOR_PRINT = 2; - - private final int mIndex; - private final int mWidth; - private final int mHeight; - - private long mNativePage; - - private Page(int index) { - Point size = mTempPoint; - synchronized (sPdfiumLock) { - mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size); - } - mIndex = index; - mWidth = size.x; - mHeight = size.y; - mCloseGuard.open("close"); - } - - /** - * Gets the page index. - * - * @return The index. - */ - public int getIndex() { - return mIndex; - } - - /** - * Gets the page width in points (1/72"). - * - * @return The width in points. - */ - public int getWidth() { - return mWidth; - } - - /** - * Gets the page height in points (1/72"). - * - * @return The height in points. - */ - public int getHeight() { - return mHeight; - } - - /** - * Renders a page to a bitmap. - * <p> - * You may optionally specify a rectangular clip in the bitmap bounds. No rendering - * outside the clip will be performed, hence it is your responsibility to initialize - * the bitmap outside the clip. - * </p> - * <p> - * You may optionally specify a matrix to transform the content from page coordinates - * which are in points (1/72") to bitmap coordinates which are in pixels. If this - * matrix is not provided this method will apply a transformation that will fit the - * whole page to the destination clip if provided or the destination bitmap if no - * clip is provided. - * </p> - * <p> - * The clip and transformation are useful for implementing tile rendering where the - * destination bitmap contains a portion of the image, for example when zooming. - * Another useful application is for printing where the size of the bitmap holding - * the page is too large and a client can render the page in stripes. - * </p> - * <p> - * <strong>Note: </strong> The destination bitmap format must be - * {@link Config#ARGB_8888 ARGB}. - * </p> - * <p> - * <strong>Note: </strong> The optional transformation matrix must be affine as per - * {@link android.graphics.Matrix#isAffine() Matrix.isAffine()}. Hence, you can specify - * rotation, scaling, translation but not a perspective transformation. - * </p> - * - * @param destination Destination bitmap to which to render. - * @param destClip Optional clip in the bitmap bounds. - * @param transform Optional transformation to apply when rendering. - * @param renderMode The render mode. - * - * @see #RENDER_MODE_FOR_DISPLAY - * @see #RENDER_MODE_FOR_PRINT - */ - public void render(@NonNull Bitmap destination, @Nullable Rect destClip, - @Nullable Matrix transform, @RenderMode int renderMode) { - if (mNativePage == 0) { - throw new NullPointerException(); - } - - destination = Preconditions.checkNotNull(destination, "bitmap null"); - - if (destination.getConfig() != Config.ARGB_8888) { - throw new IllegalArgumentException("Unsupported pixel format"); - } - - if (destClip != null) { - if (destClip.left < 0 || destClip.top < 0 - || destClip.right > destination.getWidth() - || destClip.bottom > destination.getHeight()) { - throw new IllegalArgumentException("destBounds not in destination"); - } - } - - if (transform != null && !transform.isAffine()) { - throw new IllegalArgumentException("transform not affine"); - } - - if (renderMode != RENDER_MODE_FOR_PRINT && renderMode != RENDER_MODE_FOR_DISPLAY) { - throw new IllegalArgumentException("Unsupported render mode"); - } - - if (renderMode == RENDER_MODE_FOR_PRINT && renderMode == RENDER_MODE_FOR_DISPLAY) { - throw new IllegalArgumentException("Only single render mode supported"); - } - - final int contentLeft = (destClip != null) ? destClip.left : 0; - final int contentTop = (destClip != null) ? destClip.top : 0; - final int contentRight = (destClip != null) ? destClip.right - : destination.getWidth(); - final int contentBottom = (destClip != null) ? destClip.bottom - : destination.getHeight(); - - // If transform is not set, stretch page to whole clipped area - if (transform == null) { - int clipWidth = contentRight - contentLeft; - int clipHeight = contentBottom - contentTop; - - transform = new Matrix(); - transform.postScale((float)clipWidth / getWidth(), - (float)clipHeight / getHeight()); - transform.postTranslate(contentLeft, contentTop); - } - - // FIXME: This code is planned to be outside the UI rendering module, so it should not - // be able to access native instances from Bitmap, Matrix, etc. - final long transformPtr = transform.ni(); - - synchronized (sPdfiumLock) { - nativeRenderPage(mNativeDocument, mNativePage, destination.getNativeInstance(), - contentLeft, contentTop, contentRight, contentBottom, transformPtr, - renderMode); - } - } - - /** - * Closes this page. - * - * @see android.graphics.pdf.PdfRenderer#openPage(int) - */ - @Override - public void close() { - throwIfClosed(); - doClose(); - } - - @Override - protected void finalize() throws Throwable { - try { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - - doClose(); - } finally { - super.finalize(); - } - } - - private void doClose() { - if (mNativePage != 0) { - synchronized (sPdfiumLock) { - nativeClosePage(mNativePage); - } - mNativePage = 0; - } - - mCloseGuard.close(); - mCurrentPage = null; - } - - private void throwIfClosed() { - if (mNativePage == 0) { - throw new IllegalStateException("Already closed"); - } - } - } - - private static native long nativeCreate(int fd, long size); - private static native void nativeClose(long documentPtr); - private static native int nativeGetPageCount(long documentPtr); - private static native boolean nativeScaleForPrinting(long documentPtr); - private static native void nativeRenderPage(long documentPtr, long pagePtr, long bitmapHandle, - int clipLeft, int clipTop, int clipRight, int clipBottom, long transformPtr, - int renderMode); - private static native long nativeOpenPageAndGetSize(long documentPtr, int pageIndex, - Point outSize); - private static native void nativeClosePage(long pagePtr); -} 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 dffcc6df79d8..b5ea1b1b43ea 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 @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; @@ -929,7 +930,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { Slog.e(TAG, "Duplicate call to finish"); return; } - if (!toHome) { + + boolean returningToApp = !toHome + && !mWillFinishToHome + && mPausingTasks != null + && mState == STATE_NORMAL; + if (returningToApp && allAppsAreTranslucent(mPausingTasks)) { + mHomeTransitionObserver.notifyHomeVisibilityChanged(true); + } else if (!toHome) { // For some transitions, we may have notified home activity that it became visible. // We need to notify the observer that we are no longer going home. mHomeTransitionObserver.notifyHomeVisibilityChanged(false); @@ -948,7 +956,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { if (toHome) wct.reorder(mRecentsTask, true /* toTop */); else wct.restoreTransientOrder(mRecentsTask); } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { + if (returningToApp) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app"); // The gesture is returning to the pausing-task(s) rather than continuing with // recents, so end the transition by moving the app back to the top (and also @@ -1048,6 +1056,18 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } + private boolean allAppsAreTranslucent(ArrayList<TaskState> tasks) { + if (tasks == null || tasks.isEmpty()) { + return false; + } + for (int i = tasks.size() - 1; i >= 0; --i) { + if (!tasks.get(i).mIsTranslucent) { + return false; + } + } + return true; + } + private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct, SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) { if (!sendUserLeaveHint && task.isLeaf()) { @@ -1118,6 +1138,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { /** The surface/leash of the task provided by Core. */ SurfaceControl mTaskSurface; + /** True when the task is translucent. */ + final boolean mIsTranslucent; + /** The (local) animation-leash created for this task. Only non-null for leafs. */ @Nullable SurfaceControl mLeash; @@ -1126,6 +1149,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mToken = change.getContainer(); mTaskInfo = change.getTaskInfo(); mTaskSurface = change.getLeash(); + mIsTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0; mLeash = leash; } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4e330da417be..e4f3e2defb25 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -427,7 +427,6 @@ cc_defaults { "jni/MovieImpl.cpp", "jni/pdf/PdfDocument.cpp", "jni/pdf/PdfEditor.cpp", - "jni/pdf/PdfRenderer.cpp", "jni/pdf/PdfUtils.cpp", ], shared_libs: [ diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 14b8d8d0aa12..0b739c361d64 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -70,6 +70,8 @@ public: : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {} Clip(const SkPath& path, SkClipOp op, const SkMatrix& m) : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {} + Clip(const sk_sp<SkShader> shader, SkClipOp op, const SkMatrix& m) + : mType(Type::Shader), mOp(op), mMatrix(m), mShader(shader) {} void apply(SkCanvas* canvas) const { canvas->setMatrix(mMatrix); @@ -86,6 +88,8 @@ public: // Ensure path clips are anti-aliased canvas->clipPath(mPath.value(), mOp, true); break; + case Type::Shader: + canvas->clipShader(mShader, mOp); } } @@ -94,6 +98,7 @@ private: Rect, RRect, Path, + Shader, }; Type mType; @@ -103,6 +108,7 @@ private: // These are logically a union (tracked separately due to non-POD path). std::optional<SkPath> mPath; SkRRect mRRect; + sk_sp<SkShader> mShader; }; Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { @@ -413,6 +419,11 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) { return !mCanvas->isClipEmpty(); } +void SkiaCanvas::clipShader(sk_sp<SkShader> shader, SkClipOp op) { + this->recordClip(shader, op); + mCanvas->clipShader(shader, op); +} + bool SkiaCanvas::replaceClipRect_deprecated(float left, float top, float right, float bottom) { SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 5e3553bbbbb4..4a012bc601c9 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -97,6 +97,7 @@ public: virtual bool quickRejectPath(const SkPath& path) const override; virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override; virtual bool clipPath(const SkPath* path, SkClipOp op) override; + virtual void clipShader(sk_sp<SkShader> shader, SkClipOp op) override; virtual bool replaceClipRect_deprecated(float left, float top, float right, float bottom) override; virtual bool replaceClipPath_deprecated(const SkPath* path) override; diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 883f273b5d3d..fb0cdb034575 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -70,7 +70,6 @@ extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); -extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); extern int register_android_graphics_text_MeasuredText(JNIEnv* env); extern int register_android_graphics_text_LineBreaker(JNIEnv *env); extern int register_android_graphics_text_TextShaper(JNIEnv *env); @@ -142,7 +141,6 @@ extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env); REG_JNI(register_android_graphics_fonts_FontFamily), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), - REG_JNI(register_android_graphics_pdf_PdfRenderer), REG_JNI(register_android_graphics_text_MeasuredText), REG_JNI(register_android_graphics_text_LineBreaker), REG_JNI(register_android_graphics_text_TextShaper), diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 20e3ad2c8202..14b4f584f0f3 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -188,6 +188,7 @@ public: virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0; virtual bool clipPath(const SkPath* path, SkClipOp op) = 0; + virtual void clipShader(sk_sp<SkShader> shader, SkClipOp op) = 0; // Resets clip to wide open, used to emulate the now-removed SkClipOp::kReplace on // apps with compatibility < P. Canvases for version P and later are restricted to // intersect and difference at the Java level, matching SkClipOp's definition. diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index e5bdeeea75be..1fc34d633370 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -261,6 +261,23 @@ static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pat return nonEmptyClip ? JNI_TRUE : JNI_FALSE; } +static void clipShader(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong shaderHandle, + jint opHandle) { + SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle); + sk_sp<SkShader> shader = sk_ref_sp(reinterpret_cast<SkShader*>(shaderHandle)); + switch (rgnOp) { + case SkRegion::Op::kIntersect_Op: + case SkRegion::Op::kDifference_Op: + get_canvas(canvasHandle)->clipShader(shader, static_cast<SkClipOp>(rgnOp)); + break; + default: + ALOGW("Ignoring unsupported clip operation %d", opHandle); + SkRect clipBounds; // ignored + get_canvas(canvasHandle)->getClipBounds(&clipBounds); + break; + } +} + static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) { SkBlendMode mode = static_cast<SkBlendMode>(modeHandle); get_canvas(canvasHandle)->drawColor(color, mode); @@ -797,6 +814,7 @@ static const JNINativeMethod gMethods[] = { {"nQuickReject", "(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, {"nClipRect", "(JFFFFI)Z", (void*)CanvasJNI::clipRect}, {"nClipPath", "(JJI)Z", (void*)CanvasJNI::clipPath}, + {"nClipShader", "(JJI)V", (void*)CanvasJNI::clipShader}, {"nSetDrawFilter", "(JJ)V", (void*)CanvasJNI::setPaintFilter}, }; diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp deleted file mode 100644 index cc1f96197c74..000000000000 --- a/libs/hwui/jni/pdf/PdfRenderer.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PdfUtils.h" - -#include "GraphicsJNI.h" -#include "SkBitmap.h" -#include "SkMatrix.h" -#include "fpdfview.h" - -#include <vector> -#include <utils/Log.h> -#include <unistd.h> -#include <sys/types.h> -#include <unistd.h> - -namespace android { - -static const int RENDER_MODE_FOR_DISPLAY = 1; -static const int RENDER_MODE_FOR_PRINT = 2; - -static struct { - jfieldID x; - jfieldID y; -} gPointClassInfo; - -static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr, - jint pageIndex, jobject outSize) { - FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); - - FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); - if (!page) { - jniThrowException(env, "java/lang/IllegalStateException", - "cannot load page"); - return -1; - } - - double width = 0; - double height = 0; - - int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); - if (!result) { - jniThrowException(env, "java/lang/IllegalStateException", - "cannot get page size"); - return -1; - } - - env->SetIntField(outSize, gPointClassInfo.x, width); - env->SetIntField(outSize, gPointClassInfo.y, height); - - return reinterpret_cast<jlong>(page); -} - -static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { - FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); - FPDF_ClosePage(page); -} - -static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, - jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, - jlong transformPtr, jint renderMode) { - FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); - - SkBitmap skBitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap); - - const int stride = skBitmap.width() * 4; - - FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(), - FPDFBitmap_BGRA, skBitmap.getPixels(), stride); - - int renderFlags = FPDF_REVERSE_BYTE_ORDER; - if (renderMode == RENDER_MODE_FOR_DISPLAY) { - renderFlags |= FPDF_LCD_TEXT; - } else if (renderMode == RENDER_MODE_FOR_PRINT) { - renderFlags |= FPDF_PRINTING; - } - - SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr); - SkScalar transformValues[6]; - if (!matrix.asAffine(transformValues)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "transform matrix has perspective. Only affine matrices are allowed."); - return; - } - - FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], - transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], - transformValues[SkMatrix::kATransX], - transformValues[SkMatrix::kATransY]}; - - FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; - - FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags); - - skBitmap.notifyPixelsChanged(); -} - -static const JNINativeMethod gPdfRenderer_Methods[] = { - {"nativeCreate", "(IJ)J", (void*) nativeOpen}, - {"nativeClose", "(J)V", (void*) nativeClose}, - {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, - {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, - {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, - {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, - {"nativeClosePage", "(J)V", (void*) nativeClosePage} -}; - -int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) { - int result = RegisterMethodsOrDie( - env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods, - NELEM(gPdfRenderer_Methods)); - - jclass clazz = FindClassOrDie(env, "android/graphics/Point"); - gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I"); - gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I"); - - return result; -}; - -}; diff --git a/location/api/current.txt b/location/api/current.txt index 5ed8c3c8eb4b..85e9f65a0718 100644 --- a/location/api/current.txt +++ b/location/api/current.txt @@ -88,12 +88,12 @@ package android.location { public final class Geocoder { ctor public Geocoder(@NonNull android.content.Context); ctor public Geocoder(@NonNull android.content.Context, @NonNull java.util.Locale); - method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int) throws java.io.IOException; - method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener); - method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int) throws java.io.IOException; - method public void getFromLocationName(@NonNull String, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener); - method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException; - method public void getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener); + method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int) throws java.io.IOException; + method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener); + method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int) throws java.io.IOException; + method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener); + method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException; + method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener); method public static boolean isPresent(); } diff --git a/location/api/system-current.txt b/location/api/system-current.txt index b1cf96d41497..2e7a541ecb60 100644 --- a/location/api/system-current.txt +++ b/location/api/system-current.txt @@ -591,6 +591,36 @@ package android.location { package android.location.provider { + @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getCallingAttributionTag(); + method @NonNull public String getCallingPackage(); + method public int getCallingUid(); + method @NonNull public java.util.Locale getLocale(); + method @NonNull public String getLocationName(); + method @FloatRange(from=-90.0, to=90.0) public double getLowerLeftLatitude(); + method @FloatRange(from=-180.0, to=180.0) public double getLowerLeftLongitude(); + method @IntRange(from=1) public int getMaxResults(); + method @FloatRange(from=-90.0, to=90.0) public double getUpperRightLatitude(); + method @FloatRange(from=-180.0, to=180.0) public double getUpperRightLongitude(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ForwardGeocodeRequest> CREATOR; + } + + public static final class ForwardGeocodeRequest.Builder { + ctor public ForwardGeocodeRequest.Builder(@NonNull String, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull java.util.Locale, int, @NonNull String); + method @NonNull public android.location.provider.ForwardGeocodeRequest build(); + method @NonNull public android.location.provider.ForwardGeocodeRequest.Builder setCallingAttributionTag(@NonNull String); + } + + @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public abstract class GeocodeProviderBase { + ctor public GeocodeProviderBase(@NonNull android.content.Context, @NonNull String); + method @NonNull public final android.os.IBinder getBinder(); + method public abstract void onForwardGeocode(@NonNull android.location.provider.ForwardGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>); + method public abstract void onReverseGeocode(@NonNull android.location.provider.ReverseGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>); + field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider"; + } + public abstract class LocationProviderBase { ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties); method @Nullable public final android.os.IBinder getBinder(); @@ -642,5 +672,24 @@ package android.location.provider { method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest); } + @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ReverseGeocodeRequest implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getCallingAttributionTag(); + method @NonNull public String getCallingPackage(); + method public int getCallingUid(); + method @FloatRange(from=-90.0, to=90.0) public double getLatitude(); + method @NonNull public java.util.Locale getLocale(); + method @FloatRange(from=-180.0, to=180.0) public double getLongitude(); + method @IntRange(from=1) public int getMaxResults(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ReverseGeocodeRequest> CREATOR; + } + + public static final class ReverseGeocodeRequest.Builder { + ctor public ReverseGeocodeRequest.Builder(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=0) int, @NonNull java.util.Locale, int, @NonNull String); + method @NonNull public android.location.provider.ReverseGeocodeRequest build(); + method @NonNull public android.location.provider.ReverseGeocodeRequest.Builder setCallingAttributionTag(@NonNull String); + } + } diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java index a15834407315..cdde7c66e268 100644 --- a/location/java/android/location/Geocoder.java +++ b/location/java/android/location/Geocoder.java @@ -21,11 +21,13 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.IGeocodeCallback; +import android.location.provider.ReverseGeocodeRequest; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import com.android.internal.util.Preconditions; - import java.io.IOException; import java.util.Collections; import java.util.List; @@ -33,6 +35,7 @@ import java.util.Locale; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A class for handling geocoding and reverse geocoding. Geocoding is the process of transforming a @@ -42,9 +45,12 @@ import java.util.concurrent.TimeUnit; * example one might contain the full street address of the closest building, while another might * contain only a city name and postal code. * - * The Geocoder class requires a backend service that is not included in the core android framework. - * The Geocoder query methods will return an empty list if there no backend service in the platform. - * Use the isPresent() method to determine whether a Geocoder implementation exists. + * <p>Use the isPresent() method to determine whether a Geocoder implementation exists on the + * current device. If no implementation is present, any attempt to geocode will result in an error. + * + * <p>Geocoder implementations are only required to make a best effort to return results in the + * chosen locale. Note that geocoder implementations may return results in other locales if they + * have no information available for the chosen locale. * * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or @@ -52,45 +58,53 @@ import java.util.concurrent.TimeUnit; */ public final class Geocoder { - /** A listener for asynchronous geocoding results. */ + /** + * A listener for asynchronous geocoding results. Only one of the methods will ever be invoked + * per geocoding attempt. There are no guarantees on how long it will take for a method to be + * invoked, nor any guarantees on the format or availability of error information. + */ public interface GeocodeListener { /** Invoked when geocoding completes successfully. May return an empty list. */ void onGeocode(@NonNull List<Address> addresses); - /** Invoked when geocoding fails, with a brief error message. */ + + /** Invoked when geocoding fails, with an optional error message. */ default void onError(@Nullable String errorMessage) {} } - private static final long TIMEOUT_MS = 60000; + private static final long TIMEOUT_MS = 15000; - private final GeocoderParams mParams; + private final Context mContext; + private final Locale mLocale; private final ILocationManager mService; /** - * Returns true if there is a geocoder implementation present that may return results. If true, - * there is still no guarantee that any individual geocoding attempt will succeed. + * Returns true if there is a geocoder implementation present on the device that may return + * results. If true, there is still no guarantee that any individual geocoding attempt will + * succeed. */ public static boolean isPresent() { ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface( ServiceManager.getService(Context.LOCATION_SERVICE))); try { - return lm.geocoderIsPresent(); + return lm.isGeocodeAvailable(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - /** - * Constructs a Geocoder localized for the default locale. - */ + /** Constructs a Geocoder localized for {@link Locale#getDefault()}. */ public Geocoder(@NonNull Context context) { this(context, Locale.getDefault()); } /** - * Constructs a Geocoder localized for the given locale. + * Constructs a Geocoder localized for the given locale. Note that geocoder implementations will + * only make a best effort to return results in the given locale, and there is no guarantee that + * returned results will be in the specific locale. */ public Geocoder(@NonNull Context context, @NonNull Locale locale) { - mParams = new GeocoderParams(context, locale); + mContext = Objects.requireNonNull(context); + mLocale = Objects.requireNonNull(locale); mService = ILocationManager.Stub.asInterface( ServiceManager.getService(Context.LOCATION_SERVICE)); } @@ -103,31 +117,28 @@ public final class Geocoder { * <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for - * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the - * asynchronous version of this API. If that is not possible, this should be run on a background - * thread to avoid blocking other operations.</p> + * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this + * API. If that is not possible, this should be run on a background thread to avoid blocking + * other operations. * * @param latitude the latitude a point for the search * @param longitude the longitude a point for the search * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended - * - * @return a list of Address objects. Returns null or empty list if no matches were - * found or there is no backend service available. - * + * @return a list of Address objects. Returns null or empty list if no matches were found or + * there is no backend service available. * @throws IllegalArgumentException if latitude or longitude is invalid * @throws IOException if there is a failure - * * @deprecated Use {@link #getFromLocation(double, double, int, GeocodeListener)} instead to - * avoid blocking a thread waiting for results. + * avoid blocking a thread waiting for results. */ @Deprecated public @Nullable List<Address> getFromLocation( @FloatRange(from = -90D, to = 90D) double latitude, - @FloatRange(from = -180D, to = 180D)double longitude, - @IntRange int maxResults) + @FloatRange(from = -180D, to = 180D) double longitude, + @IntRange(from = 1) int maxResults) throws IOException { SynchronousGeocoder listener = new SynchronousGeocoder(); getFromLocation(latitude, longitude, maxResults, listener); @@ -142,26 +153,32 @@ public final class Geocoder { * <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * * @param latitude the latitude a point for the search * @param longitude the longitude a point for the search * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended * @param listener a listener for receiving results - * * @throws IllegalArgumentException if latitude or longitude is invalid */ public void getFromLocation( @FloatRange(from = -90D, to = 90D) double latitude, @FloatRange(from = -180D, to = 180D) double longitude, - @IntRange int maxResults, + @IntRange(from = 1) int maxResults, @NonNull GeocodeListener listener) { - Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude"); - Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude"); - + ReverseGeocodeRequest.Builder b = + new ReverseGeocodeRequest.Builder( + latitude, + longitude, + maxResults, + mLocale, + Process.myUid(), + mContext.getPackageName()); + if (mContext.getAttributionTag() != null) { + b.setCallingAttributionTag(mContext.getAttributionTag()); + } try { - mService.getFromLocation(latitude, longitude, maxResults, mParams, - new GeocoderImpl(listener)); + mService.reverseGeocode(b.build(), new GeocodeCallbackImpl(listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -176,29 +193,25 @@ public final class Geocoder { * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for - * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the - * asynchronous version of this API. If that is not possible, this should be run on a background - * thread to avoid blocking other operations.</p> + * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this + * API. If that is not possible, this should be run on a background thread to avoid blocking + * other operations. * * @param locationName a user-supplied description of a location * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended - * - * @return a list of Address objects. Returns null or empty list if no matches were - * found or there is no backend service available. - * + * @return a list of Address objects. Returns null or empty list if no matches were found or + * there is no backend service available. * @throws IllegalArgumentException if locationName is null * @throws IOException if there is a failure - * * @deprecated Use {@link #getFromLocationName(String, int, GeocodeListener)} instead to avoid - * blocking a thread waiting for results. + * blocking a thread waiting for results. */ @Deprecated public @Nullable List<Address> getFromLocationName( - @NonNull String locationName, - @IntRange int maxResults) throws IOException { + @NonNull String locationName, @IntRange(from = 1) int maxResults) throws IOException { return getFromLocationName(locationName, maxResults, 0, 0, 0, 0); } @@ -211,17 +224,16 @@ public final class Geocoder { * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * * @param locationName a user-supplied description of a location * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended * @param listener a listener for receiving results - * * @throws IllegalArgumentException if locationName is null */ public void getFromLocationName( @NonNull String locationName, - @IntRange int maxResults, + @IntRange(from = 1) int maxResults, @NonNull GeocodeListener listener) { getFromLocationName(locationName, maxResults, 0, 0, 0, 0, listener); } @@ -232,45 +244,42 @@ public final class Geocoder { * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be * localized for the locale provided to this class's constructor. * - * <p> You may specify a bounding box for the search results by including the latitude and + * <p>You may specify a bounding box for the search results by including the latitude and * longitude of the lower left point and upper right point of the box. * * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * * <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for - * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the - * asynchronous version of this API. If that is not possible, this should be run on a background - * thread to avoid blocking other operations.</p> + * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this + * API. If that is not possible, this should be run on a background thread to avoid blocking + * other operations. * - * @param locationName a user-supplied description of a location - * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are - * recommended - * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box - * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box - * @param upperRightLatitude the latitude of the upper right corner of the bounding box + * @param locationName a user-supplied description of a location + * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended + * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box + * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box + * @param upperRightLatitude the latitude of the upper right corner of the bounding box * @param upperRightLongitude the longitude of the upper right corner of the bounding box - * - * @return a list of Address objects. Returns null or empty list if no matches were - * found or there is no backend service available. - * + * @return a list of Address objects. Returns null or empty list if no matches were found or + * there is no backend service available. * @throws IllegalArgumentException if locationName is null * @throws IllegalArgumentException if any latitude or longitude is invalid - * @throws IOException if there is a failure - * + * @throws IOException if there is a failure * @deprecated Use {@link #getFromLocationName(String, int, double, double, double, double, - * GeocodeListener)} instead to avoid blocking a thread waiting for results. + * GeocodeListener)} instead to avoid blocking a thread waiting for results. */ @Deprecated public @Nullable List<Address> getFromLocationName( @NonNull String locationName, - @IntRange int maxResults, + @IntRange(from = 1) int maxResults, @FloatRange(from = -90D, to = 90D) double lowerLeftLatitude, @FloatRange(from = -180D, to = 180D) double lowerLeftLongitude, @FloatRange(from = -90D, to = 90D) double upperRightLatitude, - @FloatRange(from = -180D, to = 180D) double upperRightLongitude) throws IOException { + @FloatRange(from = -180D, to = 180D) double upperRightLongitude) + throws IOException { SynchronousGeocoder listener = new SynchronousGeocoder(); getFromLocationName(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, listener); @@ -283,75 +292,79 @@ public final class Geocoder { * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be * localized for the locale provided to this class's constructor. * - * <p> You may specify a bounding box for the search results by including the latitude and + * <p>You may specify a bounding box for the search results by including the latitude and * longitude of the lower left point and upper right point of the box. * * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance - * purposes.</p> + * purposes. * - * @param locationName a user-supplied description of a location - * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are - * recommended - * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box - * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box - * @param upperRightLatitude the latitude of the upper right corner of the bounding box + * @param locationName a user-supplied description of a location + * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended + * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box + * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box + * @param upperRightLatitude the latitude of the upper right corner of the bounding box * @param upperRightLongitude the longitude of the upper right corner of the bounding box - * @param listener a listener for receiving results - * + * @param listener a listener for receiving results * @throws IllegalArgumentException if locationName is null * @throws IllegalArgumentException if any latitude or longitude is invalid */ public void getFromLocationName( @NonNull String locationName, - @IntRange int maxResults, + @IntRange(from = 1) int maxResults, @FloatRange(from = -90D, to = 90D) double lowerLeftLatitude, @FloatRange(from = -180D, to = 180D) double lowerLeftLongitude, @FloatRange(from = -90D, to = 90D) double upperRightLatitude, @FloatRange(from = -180D, to = 180D) double upperRightLongitude, @NonNull GeocodeListener listener) { - Preconditions.checkArgument(locationName != null); - Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude"); - Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude"); - Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude"); - Preconditions.checkArgumentInRange(upperRightLongitude, -180.0, 180.0, - "upperRightLongitude"); - + ForwardGeocodeRequest.Builder b = + new ForwardGeocodeRequest.Builder( + locationName, + lowerLeftLatitude, + lowerLeftLongitude, + upperRightLatitude, + upperRightLongitude, + maxResults, + mLocale, + Process.myUid(), + mContext.getPackageName()); + if (mContext.getAttributionTag() != null) { + b.setCallingAttributionTag(mContext.getAttributionTag()); + } try { - mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude, - upperRightLatitude, upperRightLongitude, maxResults, mParams, - new GeocoderImpl(listener)); + mService.forwardGeocode(b.build(), new GeocodeCallbackImpl(listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - private static class GeocoderImpl extends IGeocodeListener.Stub { + private static class GeocodeCallbackImpl extends IGeocodeCallback.Stub { - private GeocodeListener mListener; + @Nullable private GeocodeListener mListener; - GeocoderImpl(GeocodeListener listener) { + GeocodeCallbackImpl(GeocodeListener listener) { mListener = Objects.requireNonNull(listener); } @Override - public void onResults(String error, List<Address> addresses) throws RemoteException { + public void onError(@Nullable String error) { if (mListener == null) { return; } - GeocodeListener listener = mListener; + mListener.onError(error); mListener = null; + } - if (error != null) { - listener.onError(error); - } else { - if (addresses == null) { - addresses = Collections.emptyList(); - } - listener.onGeocode(addresses); + @Override + public void onResults(List<Address> addresses) { + if (mListener == null) { + return; } + + mListener.onGeocode(addresses); + mListener = null; } } @@ -364,21 +377,21 @@ public final class Geocoder { SynchronousGeocoder() {} @Override - public void onGeocode(List<Address> addresses) { + public void onGeocode(@NonNull List<Address> addresses) { mResults = addresses; mLatch.countDown(); } @Override - public void onError(String errorMessage) { - mError = errorMessage; + public void onError(@Nullable String error) { + mError = error; mLatch.countDown(); } public List<Address> getResults() throws IOException { try { if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - mError = "Service not Available"; + throw new IOException(new TimeoutException()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/location/java/android/location/IGeocodeProvider.aidl b/location/java/android/location/IGeocodeProvider.aidl deleted file mode 100644 index e661ca6cca2e..000000000000 --- a/location/java/android/location/IGeocodeProvider.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009 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.location; - -import android.location.Address; -import android.location.IGeocodeListener; -import android.location.GeocoderParams; - -/** - * An interface for location providers implementing the Geocoder services. - * - * {@hide} - */ -interface IGeocodeProvider { - - oneway void getFromLocation(double latitude, double longitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener); - oneway void getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, - double upperRightLongitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener); -} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 72761ef47569..c96c11898671 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -19,13 +19,11 @@ package android.location; import android.app.PendingIntent; import android.location.Address; import android.location.Criteria; -import android.location.GeocoderParams; import android.location.Geofence; import android.location.GnssAntennaInfo; import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementRequest; -import android.location.IGeocodeListener; import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; @@ -37,8 +35,11 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationRequest; import android.location.LocationTime; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.IGeocodeCallback; import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; +import android.location.provider.ReverseGeocodeRequest; import android.os.Bundle; import android.os.ICancellationSignal; import android.os.PackageTagsList; @@ -68,13 +69,9 @@ interface ILocationManager void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag); void removeGeofence(in PendingIntent intent); - boolean geocoderIsPresent(); - void getFromLocation(double latitude, double longitude, int maxResults, - in GeocoderParams params, in IGeocodeListener listener); - void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - in GeocoderParams params, in IGeocodeListener listener); + boolean isGeocodeAvailable(); + void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback); + void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback); GnssCapabilities getGnssCapabilities(); int getGnssYearOfHardware(); diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig index 0fa641b926a9..156be389fe84 100644 --- a/location/java/android/location/flags/location.aconfig +++ b/location/java/android/location/flags/location.aconfig @@ -1,6 +1,13 @@ package: "android.location.flags" flag { + name: "new_geocoder" + namespace: "location" + description: "Flag for new Geocoder APIs" + bug: "229872126" +} + +flag { name: "location_bypass" namespace: "location" description: "Enable location bypass appops behavior" diff --git a/location/java/android/location/provider/ForwardGeocodeRequest.aidl b/location/java/android/location/provider/ForwardGeocodeRequest.aidl new file mode 100644 index 000000000000..acd6190aeec8 --- /dev/null +++ b/location/java/android/location/provider/ForwardGeocodeRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +parcelable ForwardGeocodeRequest; diff --git a/location/java/android/location/provider/ForwardGeocodeRequest.java b/location/java/android/location/provider/ForwardGeocodeRequest.java new file mode 100644 index 000000000000..8f227b1604b7 --- /dev/null +++ b/location/java/android/location/provider/ForwardGeocodeRequest.java @@ -0,0 +1,286 @@ +/* + * Copyright 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.location.provider; + +import static java.lang.Math.max; + +import android.annotation.FlaggedApi; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.location.flags.Flags; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.Locale; +import java.util.Objects; + +/** + * Forward geocode (ie from address to lat/lng) provider request. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_NEW_GEOCODER) +@SystemApi +public final class ForwardGeocodeRequest implements Parcelable { + + private final String mLocationName; + private final double mLowerLeftLatitude; + private final double mLowerLeftLongitude; + private final double mUpperRightLatitude; + private final double mUpperRightLongitude; + private final int mMaxResults; + private final Locale mLocale; + private final int mCallingUid; + private final String mCallingPackage; + @Nullable private final String mCallingAttributionTag; + + private ForwardGeocodeRequest( + @NonNull String locationName, + double lowerLeftLatitude, + double lowerLeftLongitude, + double upperRightLatitude, + double upperRightLongitude, + int maxResults, + @NonNull Locale locale, + int callingUid, + @NonNull String callingPackage, + @Nullable String callingAttributionTag) { + Preconditions.checkArgument(locationName != null, "locationName must not be null"); + Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude"); + Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude"); + Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude"); + Preconditions.checkArgumentInRange( + upperRightLongitude, -180.0, 180.0, "upperRightLongitude"); + + mLocationName = locationName; + mLowerLeftLatitude = lowerLeftLatitude; + mLowerLeftLongitude = lowerLeftLongitude; + mUpperRightLatitude = upperRightLatitude; + mUpperRightLongitude = upperRightLongitude; + mMaxResults = max(maxResults, 1); + mLocale = Objects.requireNonNull(locale); + + mCallingUid = callingUid; + mCallingPackage = Objects.requireNonNull(callingPackage); + mCallingAttributionTag = callingAttributionTag; + } + + /** + * The location name to be forward geocoded. An arbitrary user string that could have any value. + */ + @NonNull + public String getLocationName() { + return mLocationName; + } + + /** The lower left latitude of the bounding box that should constrain forward geocoding. */ + @FloatRange(from = -90.0, to = 90.0) + public double getLowerLeftLatitude() { + return mLowerLeftLatitude; + } + + /** The lower left longitude of the bounding box that should constrain forward geocoding. */ + @FloatRange(from = -180.0, to = 180.0) + public double getLowerLeftLongitude() { + return mLowerLeftLongitude; + } + + /** The upper right latitude of the bounding box that should constrain forward geocoding. */ + @FloatRange(from = -90.0, to = 90.0) + public double getUpperRightLatitude() { + return mUpperRightLatitude; + } + + /** The upper right longitude of the bounding box that should constrain forward geocoding. */ + @FloatRange(from = -180.0, to = 180.0) + public double getUpperRightLongitude() { + return mUpperRightLongitude; + } + + /** The maximum number of forward geocoding results that should be returned. */ + @IntRange(from = 1) + public int getMaxResults() { + return mMaxResults; + } + + /** The locale that results should be localized to (best effort). */ + @NonNull + public Locale getLocale() { + return mLocale; + } + + /** The UID of the caller this geocoding request is happening on behalf of. */ + public int getCallingUid() { + return mCallingUid; + } + + /** The package of the caller this geocoding request is happening on behalf of. */ + @NonNull + public String getCallingPackage() { + return mCallingPackage; + } + + /** The attribution tag of the caller this geocoding request is happening on behalf of. */ + @Nullable + public String getCallingAttributionTag() { + return mCallingAttributionTag; + } + + public static final @NonNull Creator<ForwardGeocodeRequest> CREATOR = + new Creator<>() { + @Override + public ForwardGeocodeRequest createFromParcel(Parcel in) { + return new ForwardGeocodeRequest( + Objects.requireNonNull(in.readString8()), + in.readDouble(), + in.readDouble(), + in.readDouble(), + in.readDouble(), + in.readInt(), + new Locale(in.readString8(), in.readString8(), in.readString8()), + in.readInt(), + Objects.requireNonNull(in.readString8()), + in.readString8()); + } + + @Override + public ForwardGeocodeRequest[] newArray(int size) { + return new ForwardGeocodeRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeString8(mLocationName); + parcel.writeDouble(mLowerLeftLatitude); + parcel.writeDouble(mLowerLeftLongitude); + parcel.writeDouble(mUpperRightLatitude); + parcel.writeDouble(mUpperRightLongitude); + parcel.writeInt(mMaxResults); + parcel.writeString8(mLocale.getLanguage()); + parcel.writeString8(mLocale.getCountry()); + parcel.writeString8(mLocale.getVariant()); + parcel.writeInt(mCallingUid); + parcel.writeString8(mCallingPackage); + parcel.writeString8(mCallingAttributionTag); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof ForwardGeocodeRequest that) { + return mLowerLeftLatitude == that.mLowerLeftLatitude + && mLowerLeftLongitude == that.mLowerLeftLongitude + && mUpperRightLatitude == that.mUpperRightLatitude + && mUpperRightLongitude == that.mUpperRightLongitude + && mMaxResults == that.mMaxResults + && mCallingUid == that.mCallingUid + && mLocale.equals(that.mLocale) + && mCallingPackage.equals(that.mCallingPackage) + && mLocationName.equals(that.mLocationName) + && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + mLocationName, + mLowerLeftLatitude, + mLowerLeftLongitude, + mUpperRightLatitude, + mUpperRightLongitude, + mMaxResults, + mLocale, + mCallingUid, + mCallingPackage, + mCallingAttributionTag); + } + + /** A Builder for {@link ReverseGeocodeRequest}s. */ + public static final class Builder { + + private final String mLocationName; + private final double mLowerLeftLatitude; + private final double mLowerLeftLongitude; + private final double mUpperRightLatitude; + private final double mUpperRightLongitude; + private final int mMaxResults; + private final Locale mLocale; + + private final int mCallingUid; + private final String mCallingPackage; + @Nullable private String mCallingAttributionTag; + + /** Creates a new Builder instance with the given parameters. */ + public Builder( + @NonNull String locationName, + @FloatRange(from = -90.0, to = 90.0) double lowerLeftLatitude, + @FloatRange(from = -180.0, to = 180.0) double lowerLeftLongitude, + @FloatRange(from = -90.0, to = 90.0) double upperRightLatitude, + @FloatRange(from = -180.0, to = 180.0) double upperRightLongitude, + @IntRange(from = 1) int maxResults, + @NonNull Locale locale, + int callingUid, + @NonNull String callingPackage) { + mLocationName = locationName; + mLowerLeftLatitude = lowerLeftLatitude; + mLowerLeftLongitude = lowerLeftLongitude; + mUpperRightLatitude = upperRightLatitude; + mUpperRightLongitude = upperRightLongitude; + mMaxResults = maxResults; + mLocale = locale; + mCallingUid = callingUid; + mCallingPackage = callingPackage; + mCallingAttributionTag = null; + } + + /** Sets the attribution tag. */ + @NonNull + public Builder setCallingAttributionTag(@NonNull String attributionTag) { + mCallingAttributionTag = attributionTag; + return this; + } + + /** Builds a {@link ForwardGeocodeRequest}. */ + @NonNull + public ForwardGeocodeRequest build() { + return new ForwardGeocodeRequest( + mLocationName, + mLowerLeftLatitude, + mLowerLeftLongitude, + mUpperRightLatitude, + mUpperRightLongitude, + mMaxResults, + mLocale, + mCallingUid, + mCallingPackage, + mCallingAttributionTag); + } + } +} diff --git a/location/java/android/location/provider/GeocodeProviderBase.java b/location/java/android/location/provider/GeocodeProviderBase.java new file mode 100644 index 000000000000..e2c48b9c3515 --- /dev/null +++ b/location/java/android/location/provider/GeocodeProviderBase.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 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.location.provider; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.content.Context; +import android.content.Intent; +import android.location.Address; +import android.location.flags.Flags; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.OutcomeReceiver; +import android.os.RemoteException; +import android.util.Log; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Base class for geocode providers outside the system server. + * + * <p>Geocode providers should be wrapped in a non-exported service which returns the result of + * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The + * service should not be exported so that components other than the system server cannot bind to it. + * Alternatively, the service may be guarded by a permission that only system server can obtain. The + * service may specify metadata on its capabilities: + * + * <ul> + * <li>"serviceVersion": An integer version code to help tie break if multiple services are + * capable of implementing the geocode provider. All else equal, the service with the highest + * version code will be chosen. Assumed to be 0 if not specified. + * <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take + * responsibility for handling changes to the current user on the device. If true, the service + * will always be bound from the system user. If false, the service will always be bound from + * the current user. If the current user changes, the old binding will be released, and a new + * binding established under the new user. Assumed to be false if not specified. + * </ul> + * + * <p>The service should have an intent filter in place for the geocode provider as specified by the + * constant in this class. + * + * <p>Geocode providers are identified by their UID / package name / attribution tag. Based on this + * identity, geocode providers may be given some special privileges. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_NEW_GEOCODER) +@SystemApi +public abstract class GeocodeProviderBase { + + /** + * The action the wrapping service should have in its intent filter to implement the geocode + * provider. + */ + @SuppressLint("ActionValue") + public static final String ACTION_GEOCODE_PROVIDER = + "com.android.location.service.GeocodeProvider"; + + final String mTag; + @Nullable final String mAttributionTag; + final IBinder mBinder; + + /** + * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging + * of errors, and thus should uniquely identify the class. + */ + public GeocodeProviderBase(@NonNull Context context, @NonNull String tag) { + mTag = tag; + mAttributionTag = context.getAttributionTag(); + mBinder = new Service(); + } + + /** + * Returns the IBinder instance that should be returned from the {@link + * android.app.Service#onBind(Intent)} method of the wrapping service. + */ + @NonNull + public final IBinder getBinder() { + return mBinder; + } + + /** + * Requests forward geocoding of the given arguments. The given callback must be invoked once. + */ + public abstract void onForwardGeocode( + @NonNull ForwardGeocodeRequest request, + @NonNull OutcomeReceiver<List<Address>, Exception> callback); + + /** + * Requests reverse geocoding of the given arguments. The given callback must be invoked once. + */ + public abstract void onReverseGeocode( + @NonNull ReverseGeocodeRequest request, + @NonNull OutcomeReceiver<List<Address>, Exception> callback); + + private class Service extends IGeocodeProvider.Stub { + @Override + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + try { + onForwardGeocode(request, new SingleUseCallback(callback)); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(mTag, e); + new Handler(Looper.getMainLooper()) + .post( + () -> { + throw new AssertionError(e); + }); + } + } + + @Override + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + try { + onReverseGeocode(request, new SingleUseCallback(callback)); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(mTag, e); + new Handler(Looper.getMainLooper()) + .post( + () -> { + throw new AssertionError(e); + }); + } + } + } + + private static class SingleUseCallback implements OutcomeReceiver<List<Address>, Exception> { + + private final AtomicReference<IGeocodeCallback> mCallback; + + SingleUseCallback(IGeocodeCallback callback) { + mCallback = new AtomicReference<>(callback); + } + + @Override + public void onError(Exception e) { + try { + Objects.requireNonNull(mCallback.getAndSet(null)).onError(e.toString()); + } catch (RemoteException r) { + throw r.rethrowFromSystemServer(); + } + } + + @Override + public void onResult(List<Address> results) { + try { + Objects.requireNonNull(mCallback.getAndSet(null)).onResults(results); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } +} diff --git a/location/java/android/location/IGeocodeListener.aidl b/location/java/android/location/provider/IGeocodeCallback.aidl index 8e104119a6f3..cf527130bcc3 100644 --- a/location/java/android/location/IGeocodeListener.aidl +++ b/location/java/android/location/provider/IGeocodeCallback.aidl @@ -14,16 +14,15 @@ * limitations under the License. */ -package android.location; +package android.location.provider; import android.location.Address; /** - * An interface for returning geocode results. - * - * {@hide} + * Binder interface for geocoding callbacks. + * @hide */ -interface IGeocodeListener { - - oneway void onResults(String error, in List<Address> results); +oneway interface IGeocodeCallback { + void onError(String error); + void onResults(in List<Address> results); } diff --git a/location/java/android/location/provider/IGeocodeProvider.aidl b/location/java/android/location/provider/IGeocodeProvider.aidl new file mode 100644 index 000000000000..92174387c578 --- /dev/null +++ b/location/java/android/location/provider/IGeocodeProvider.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 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.location.provider; + +import android.location.provider.IGeocodeCallback; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.ReverseGeocodeRequest; + +/** + * Binder interface for services that implement geocode providers. Do not implement this directly, + * extend {@link GeocodeProviderBase} instead. + * @hide + */ +oneway interface IGeocodeProvider { + void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback); + void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback); +} diff --git a/location/java/android/location/GeocoderParams.aidl b/location/java/android/location/provider/ReverseGeocodeRequest.aidl index 2484e207dae7..015757ad5d2d 100644 --- a/location/java/android/location/GeocoderParams.aidl +++ b/location/java/android/location/provider/ReverseGeocodeRequest.aidl @@ -1,11 +1,10 @@ /* - * Copyright (C) 2010, The Android Open Source Project - * + * Copyright (C) 2024 * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +13,6 @@ * limitations under the License. */ -package android.location; +package android.location.provider; -parcelable GeocoderParams; +parcelable ReverseGeocodeRequest; diff --git a/location/java/android/location/provider/ReverseGeocodeRequest.java b/location/java/android/location/provider/ReverseGeocodeRequest.java new file mode 100644 index 000000000000..57c9047f07ca --- /dev/null +++ b/location/java/android/location/provider/ReverseGeocodeRequest.java @@ -0,0 +1,230 @@ +/* + * Copyright 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.location.provider; + +import static java.lang.Math.max; + +import android.annotation.FlaggedApi; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.location.flags.Flags; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.Locale; +import java.util.Objects; + +/** + * Reverse geocode (ie from lat/lng to address) provider request. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_NEW_GEOCODER) +@SystemApi +public final class ReverseGeocodeRequest implements Parcelable { + + private final double mLatitude; + private final double mLongitude; + private final int mMaxResults; + private final Locale mLocale; + + private final int mCallingUid; + private final String mCallingPackage; + @Nullable private final String mCallingAttributionTag; + + private ReverseGeocodeRequest( + double latitude, + double longitude, + int maxResults, + Locale locale, + int callingUid, + String callingPackage, + @Nullable String callingAttributionTag) { + Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude"); + Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude"); + + mLatitude = latitude; + mLongitude = longitude; + mMaxResults = max(maxResults, 1); + mLocale = Objects.requireNonNull(locale); + + mCallingUid = callingUid; + mCallingPackage = Objects.requireNonNull(callingPackage); + mCallingAttributionTag = callingAttributionTag; + } + + /** The latitude of the point to be reverse geocoded. */ + @FloatRange(from = -90.0, to = 90.0) + public double getLatitude() { + return mLatitude; + } + + /** The longitude of the point to be reverse geocoded. */ + @FloatRange(from = -180.0, to = 180.0) + public double getLongitude() { + return mLongitude; + } + + /** The maximum number of reverse geocoding results that should be returned. */ + @IntRange(from = 1) + public int getMaxResults() { + return mMaxResults; + } + + /** The locale that results should be localized to (best effort). */ + @NonNull + public Locale getLocale() { + return mLocale; + } + + /** The UID of the caller this geocoding request is happening on behalf of. */ + public int getCallingUid() { + return mCallingUid; + } + + /** The package of the caller this geocoding request is happening on behalf of. */ + @NonNull + public String getCallingPackage() { + return mCallingPackage; + } + + /** The attribution tag of the caller this geocoding request is happening on behalf of. */ + @Nullable + public String getCallingAttributionTag() { + return mCallingAttributionTag; + } + + public static final @NonNull Creator<ReverseGeocodeRequest> CREATOR = + new Creator<>() { + @Override + public ReverseGeocodeRequest createFromParcel(Parcel in) { + return new ReverseGeocodeRequest( + in.readDouble(), + in.readDouble(), + in.readInt(), + new Locale(in.readString8(), in.readString8(), in.readString8()), + in.readInt(), + Objects.requireNonNull(in.readString8()), + in.readString8()); + } + + @Override + public ReverseGeocodeRequest[] newArray(int size) { + return new ReverseGeocodeRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeDouble(mLatitude); + parcel.writeDouble(mLongitude); + parcel.writeInt(mMaxResults); + parcel.writeString8(mLocale.getLanguage()); + parcel.writeString8(mLocale.getCountry()); + parcel.writeString8(mLocale.getVariant()); + parcel.writeInt(mCallingUid); + parcel.writeString8(mCallingPackage); + parcel.writeString8(mCallingAttributionTag); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof ReverseGeocodeRequest that) { + return mLatitude == that.mLatitude + && mLongitude == that.mLongitude + && mMaxResults == that.mMaxResults + && mCallingUid == that.mCallingUid + && mLocale.equals(that.mLocale) + && mCallingPackage.equals(that.mCallingPackage) + && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + mLatitude, + mLongitude, + mMaxResults, + mLocale, + mCallingUid, + mCallingPackage, + mCallingAttributionTag); + } + + /** A Builder for {@link ReverseGeocodeRequest}s. */ + public static final class Builder { + + private final double mLatitude; + private final double mLongitude; + private final int mMaxResults; + private final Locale mLocale; + + private final int mCallingUid; + private final String mCallingPackage; + @Nullable private String mCallingAttributionTag; + + /** Creates a new Builder instance with the given parameters. */ + public Builder( + @FloatRange(from = -90.0, to = 90.0) double latitude, + @FloatRange(from = -180.0, to = 180.0) double longitude, + @IntRange(from = 0) int maxResults, + @NonNull Locale locale, + int callingUid, + @NonNull String callingPackage) { + mLatitude = latitude; + mLongitude = longitude; + mMaxResults = maxResults; + mLocale = locale; + mCallingUid = callingUid; + mCallingPackage = callingPackage; + mCallingAttributionTag = null; + } + + /** Sets the attribution tag. */ + @NonNull + public Builder setCallingAttributionTag(@NonNull String attributionTag) { + mCallingAttributionTag = attributionTag; + return this; + } + + /** Builds a {@link ReverseGeocodeRequest}. */ + @NonNull + public ReverseGeocodeRequest build() { + return new ReverseGeocodeRequest( + mLatitude, + mLongitude, + mMaxResults, + mLocale, + mCallingUid, + mCallingPackage, + mCallingAttributionTag); + } + } +} diff --git a/location/lib/Android.bp b/location/lib/Android.bp index c9be5797fdbc..b10019a94209 100644 --- a/location/lib/Android.bp +++ b/location/lib/Android.bp @@ -30,6 +30,7 @@ java_sdk_library { "androidx.annotation_annotation", ], api_packages: [ + "android.location", "com.android.location.provider", "com.android.location.timezone.provider", ], diff --git a/location/lib/README.txt b/location/lib/README.txt index 400a7dd3f6ba..ae5187245163 100644 --- a/location/lib/README.txt +++ b/location/lib/README.txt @@ -1,30 +1,12 @@ This library (com.android.location.provider.jar) is a shared java library -containing classes required by unbundled location providers. - ---- Rules of this library --- -o This library is effectively a PUBLIC API for unbundled location providers - that may be distributed outside the system image. So it MUST BE API STABLE. - You can add but not remove. The rules are the same as for the - public platform SDK API. -o This library can see and instantiate internal platform classes (such as - ProviderRequest.java), but it must not expose them in any public method - (or by extending them via inheritance). This would break clients of the - library because they cannot see the internal platform classes. - -This library is distributed in the system image, and loaded as -a shared library. So you can change the implementation, but not -the interface. In this way it is like framework.jar. - ---- Why does this library exists? --- - -Unbundled location providers (such as the NetworkLocationProvider) -can not use internal platform classes. - -So ideally all of these classes would be part of the public platform SDK API, -but that doesn't seem like a great idea when only applications with a special -signature can implement this API. - -The compromise is this library. - -It wraps internal platform classes (like ProviderRequest) with a stable -API that does not leak the internal classes. +containing classes required by unbundled providers. The library was created +as a way of exposing API classes outside of the public API before SystemApi +was possible. Now that SystemApi exists, no new classes should ever be added +to this library, and all classes in this library should eventually be +deprecated and new SystemApi replacements offered. + +Whether or not classes in this library can ever be removed must be answered on +a case by case basis. Most of the classes are usually referenced by Google Play +services (in which case references can be removed from that code base), but +these classes may also be referenced by OEM code, which must be considered +before any removal. diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt index 7046abd8fd3b..75e6bb437ca4 100644 --- a/location/lib/api/system-current.txt +++ b/location/lib/api/system-current.txt @@ -1,4 +1,21 @@ // Signature format: 2.0 +package android.location { + + @Deprecated public class GeocoderParams implements android.os.Parcelable { + ctor @Deprecated public GeocoderParams(android.content.Context); + ctor @Deprecated public GeocoderParams(android.content.Context, java.util.Locale); + ctor @Deprecated public GeocoderParams(int, String, @Nullable String, java.util.Locale); + method @Deprecated public int describeContents(); + method @Deprecated @Nullable public String getClientAttributionTag(); + method @Deprecated @NonNull public String getClientPackage(); + method @Deprecated public int getClientUid(); + method @Deprecated @NonNull public java.util.Locale getLocale(); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GeocoderParams> CREATOR; + } + +} + package com.android.location.provider { @Deprecated public final class FusedLocationHardware { @@ -25,6 +42,13 @@ package com.android.location.provider { method @Deprecated public void onStatusChanged(int); } + @Deprecated public abstract class GeocodeProvider { + ctor @Deprecated public GeocodeProvider(); + method @Deprecated public android.os.IBinder getBinder(); + method @Deprecated public abstract String onGetFromLocation(double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>); + method @Deprecated public abstract String onGetFromLocationName(String, double, double, double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>); + } + @Deprecated public class GmsFusedBatchOptions { ctor @Deprecated public GmsFusedBatchOptions(); method @Deprecated public int getFlags(); diff --git a/core/java/android/location/GeocoderParams.java b/location/lib/java/android/location/GeocoderParams.java index 3ea6364e07c5..780ccf4ca53c 100644 --- a/core/java/android/location/GeocoderParams.java +++ b/location/lib/java/android/location/GeocoderParams.java @@ -18,7 +18,7 @@ package android.location; import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; @@ -28,15 +28,15 @@ import java.util.Locale; import java.util.Objects; /** - * This class contains extra parameters to pass to an IGeocodeProvider - * implementation from the Geocoder class. Currently this contains the - * language, country and variant information from the Geocoder's locale - * as well as the Geocoder client's package name for geocoder server - * logging. This information is kept in a separate class to allow for - * future expansion of the IGeocodeProvider interface. + * This class was originally shipped out-of-band from the normal API processes as a separate drop + * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively + * published through SystemApi. * + * @deprecated Do not use. * @hide */ +@Deprecated +@SystemApi public class GeocoderParams implements Parcelable { private final int mUid; @@ -52,7 +52,8 @@ public class GeocoderParams implements Parcelable { this(Process.myUid(), context.getPackageName(), context.getAttributionTag(), locale); } - private GeocoderParams(int uid, String packageName, String attributionTag, Locale locale) { + public GeocoderParams( + int uid, String packageName, @Nullable String attributionTag, Locale locale) { mUid = uid; mPackageName = Objects.requireNonNull(packageName); mAttributionTag = attributionTag; @@ -62,7 +63,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client UID. */ - @UnsupportedAppUsage public int getClientUid() { return mUid; } @@ -70,7 +70,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client package name. */ - @UnsupportedAppUsage public @NonNull String getClientPackage() { return mPackageName; } @@ -78,7 +77,6 @@ public class GeocoderParams implements Parcelable { /** * Returns the client attribution tag. */ - @UnsupportedAppUsage public @Nullable String getClientAttributionTag() { return mAttributionTag; } @@ -86,34 +84,38 @@ public class GeocoderParams implements Parcelable { /** * Returns the locale. */ - @UnsupportedAppUsage public @NonNull Locale getLocale() { return mLocale; } public static final @NonNull Parcelable.Creator<GeocoderParams> CREATOR = - new Parcelable.Creator<GeocoderParams>() { - public GeocoderParams createFromParcel(Parcel in) { - int uid = in.readInt(); - String packageName = in.readString8(); - String attributionTag = in.readString8(); - String language = in.readString8(); - String country = in.readString8(); - String variant = in.readString8(); - - return new GeocoderParams(uid, packageName, attributionTag, - new Locale(language, country, variant)); - } - - public GeocoderParams[] newArray(int size) { - return new GeocoderParams[size]; - } - }; - + new Parcelable.Creator<>() { + public GeocoderParams createFromParcel(Parcel in) { + int uid = in.readInt(); + String packageName = in.readString8(); + String attributionTag = in.readString8(); + String language = in.readString8(); + String country = in.readString8(); + String variant = in.readString8(); + + return new GeocoderParams( + uid, + packageName, + attributionTag, + new Locale(language, country, variant)); + } + + public GeocoderParams[] newArray(int size) { + return new GeocoderParams[size]; + } + }; + + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mUid); parcel.writeString8(mPackageName); diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java index 05d793542202..45077ba8adc5 100644 --- a/location/lib/java/com/android/location/provider/GeocodeProvider.java +++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java @@ -16,10 +16,13 @@ package com.android.location.provider; +import android.annotation.SystemApi; import android.location.Address; import android.location.GeocoderParams; -import android.location.IGeocodeListener; -import android.location.IGeocodeProvider; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.IGeocodeCallback; +import android.location.provider.IGeocodeProvider; +import android.location.provider.ReverseGeocodeRequest; import android.os.IBinder; import android.os.RemoteException; @@ -27,47 +30,74 @@ import java.util.ArrayList; import java.util.List; /** - * Base class for geocode providers implemented as unbundled services. + * This class was originally shipped out-of-band from the normal API processes as a separate drop + * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively + * published through SystemApi. * - * <p>Geocode providers can be implemented as services and return the result of - * {@link GeocodeProvider#getBinder()} in its getBinder() method. - * - * <p>IMPORTANT: This class is effectively a public API for unbundled - * applications, and must remain API stable. See README.txt in the root - * of this package for more information. + * @deprecated Use {@link android.location.provider.GeocodeProviderBase} instead. * @hide */ +@Deprecated +@SystemApi public abstract class GeocodeProvider { - private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() { - @Override - public void getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - List<Address> results = new ArrayList<>(); - String error = onGetFromLocation(latitude, longitude, maxResults, params, results); - try { - listener.onResults(error, results); - } catch (RemoteException e) { - // ignore - } - } + private final IGeocodeProvider.Stub mProvider = + new IGeocodeProvider.Stub() { + @Override + public void reverseGeocode( + ReverseGeocodeRequest request, IGeocodeCallback callback) { + List<Address> results = new ArrayList<>(); + String error = + onGetFromLocation( + request.getLatitude(), + request.getLongitude(), + request.getMaxResults(), + new GeocoderParams( + request.getCallingUid(), + request.getCallingPackage(), + request.getCallingAttributionTag(), + request.getLocale()), + results); + try { + if (error != null) { + callback.onError(error); + } else { + callback.onResults(results); + } + } catch (RemoteException e) { + // ignore + } + } - @Override - public void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - List<Address> results = new ArrayList<>(); - String error = onGetFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, results); - try { - listener.onResults(error, results); - } catch (RemoteException e) { - // ignore - } - } - }; + @Override + public void forwardGeocode( + ForwardGeocodeRequest request, IGeocodeCallback callback) { + List<Address> results = new ArrayList<>(); + String error = + onGetFromLocationName( + request.getLocationName(), + request.getLowerLeftLatitude(), + request.getLowerLeftLongitude(), + request.getUpperRightLatitude(), + request.getUpperRightLongitude(), + request.getMaxResults(), + new GeocoderParams( + request.getCallingUid(), + request.getCallingPackage(), + request.getCallingAttributionTag(), + request.getLocale()), + results); + try { + if (error != null) { + callback.onError(error); + } else { + callback.onResults(results); + } + } catch (RemoteException e) { + // ignore + } + } + }; /** * This method is overridden to implement the diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index ac94a6f6ad3c..9548525d68d1 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -3557,6 +3557,36 @@ final public class MediaCodec { } /** + * Set a linear block that contain multiple non-encrypted access unit to this + * queue request. Exactly one buffer must be set for a queue request before + * calling {@link #queue}. Multiple access units if present must be laid out contiguously + * and without gaps and in order. An IllegalArgumentException will be thrown + * during {@link #queue} if access units are not laid out contiguously. + * + * @param block The linear block object + * @param infos Represents {@link MediaCodec.BufferInfo} objects to mark + * individual access-unit boundaries and the timestamps associated with it. + * @return this object + * @throws IllegalStateException if a buffer is already set + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public @NonNull QueueRequest setMultiFrameLinearBlock( + @NonNull LinearBlock block, + @NonNull ArrayDeque<BufferInfo> infos) { + if (!isAccessible()) { + throw new IllegalStateException("The request is stale"); + } + if (mLinearBlock != null || mHardwareBuffer != null) { + throw new IllegalStateException("Cannot set block twice"); + } + mLinearBlock = block; + mBufferInfos.clear(); + mBufferInfos.addAll(infos); + mCryptoInfos.clear(); + return this; + } + + /** * Set an encrypted linear block to this queue request. Exactly one buffer must be * set for a queue request before calling {@link #queue}. It is possible * to use the same {@link LinearBlock} object for multiple queue @@ -3691,26 +3721,6 @@ final public class MediaCodec { } /** - * Sets MediaCodec.BufferInfo objects describing the access units - * contained in this queue request. Access units must be laid out - * contiguously without gaps and in order. - * - * @param infos Represents {@link MediaCodec.BufferInfo} objects to mark - * individual access-unit boundaries and the timestamps associated with it. - * The buffer is expected to contain the data in a continuous manner. - * @return this object - */ - @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) - public @NonNull QueueRequest setBufferInfos(@NonNull ArrayDeque<BufferInfo> infos) { - if (!isAccessible()) { - throw new IllegalStateException("The request is stale"); - } - mBufferInfos.clear(); - mBufferInfos.addAll(infos); - return this; - } - - /** * Add an integer parameter. * See {@link MediaFormat} for an exhaustive list of supported keys with * values of type int, that can also be set with {@link MediaFormat#setInteger}. diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 3174c377b91c..1e7bc4764dd7 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -20,10 +20,12 @@ import static android.media.Utils.intersectSortedDistinctRanges; import static android.media.Utils.sortDistinctRanges; import static android.media.codec.Flags.FLAG_DYNAMIC_COLOR_ASPECTS; import static android.media.codec.Flags.FLAG_HLG_EDITING; +import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +42,8 @@ import android.util.Range; import android.util.Rational; import android.util.Size; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1808,6 +1812,55 @@ public final class MediaCodecInfo { } } + /** @hide */ + @IntDef(prefix = {"SECURITY_MODEL_"}, value = { + SECURITY_MODEL_SANDBOXED, + SECURITY_MODEL_MEMORY_SAFE, + SECURITY_MODEL_TRUSTED_CONTENT_ONLY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SecurityModel {} + + /** + * In this model the codec is running in a sandboxed process. Even if a + * malicious content was fed to the codecs in this model, the impact will + * be contained in the sandboxed process. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int SECURITY_MODEL_SANDBOXED = 0; + /** + * In this model the codec is not running in a sandboxed process, but + * written in a memory-safe way. It typically means that the software + * implementation of the codec is written in a memory-safe language such + * as Rust. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int SECURITY_MODEL_MEMORY_SAFE = 1; + /** + * In this model the codec is suitable only for trusted content where + * the input can be verified to be well-formed and no malicious actor + * can alter it. For example, codecs in this model are not suitable + * for arbitrary media downloaded from the internet or present in a user + * directory. On the other hand, they could be suitable for media encoded + * in the backend that the app developer wholly controls. + * <p> + * Codecs with this security model is not included in + * {@link MediaCodecList#REGULAR_CODECS}, but included in + * {@link MediaCodecList#ALL_CODECS}. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; + + /** + * Query the security model of the codec. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + @SecurityModel + public int getSecurityModel() { + // TODO b/297922713 --- detect security model of out-of-sandbox codecs + return SECURITY_MODEL_SANDBOXED; + } + /** * A class that supports querying the video capabilities of a codec. */ diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 5e40eee26886..7b83842a9fb2 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -16,6 +16,8 @@ package android.media; +import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; + import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE; import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; @@ -1715,6 +1717,58 @@ public final class MediaFormat { @FlaggedApi(FLAG_CODEC_IMPORTANCE) public static final String KEY_IMPORTANCE = "importance"; + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_SECURITY_MODEL_"}, value = { + FLAG_SECURITY_MODEL_SANDBOXED, + FLAG_SECURITY_MODEL_MEMORY_SAFE, + FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SecurityModelFlag {} + + /** + * Flag for {@link MediaCodecInfo#SECURITY_MODEL_SANDBOXED}. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int FLAG_SECURITY_MODEL_SANDBOXED = + (1 << MediaCodecInfo.SECURITY_MODEL_SANDBOXED); + /** + * Flag for {@link MediaCodecInfo#SECURITY_MODEL_MEMORY_SAFE}. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE = + (1 << MediaCodecInfo.SECURITY_MODEL_MEMORY_SAFE); + /** + * Flag for {@link MediaCodecInfo#SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = + (1 << MediaCodecInfo.SECURITY_MODEL_TRUSTED_CONTENT_ONLY); + + /** + * A key describing the requested security model as flags. + * <p> + * The associated value is a flag of the following values: + * {@link FLAG_SECURITY_MODEL_SANDBOXED}, + * {@link FLAG_SECURITY_MODEL_MEMORY_SAFE}, + * {@link FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. The default value is + * {@link FLAG_SECURITY_MODEL_SANDBOXED}. + * <p> + * When passed to {@link MediaCodecList#findDecoderForFormat} or + * {@link MediaCodecList#findEncoderForFormat}, MediaCodecList filters + * the security model of the codecs according to this flag value. + * <p> + * When passed to {@link MediaCodec#configure}, MediaCodec verifies + * the security model matches the flag value passed, and throws + * {@link java.lang.IllegalArgumentException} if the model does not match. + * <p> + * @see MediaCodecInfo#getSecurityModel + * @see MediaCodecList#findDecoderForFormat + * @see MediaCodecList#findEncoderForFormat + */ + @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) + public static final String KEY_SECURITY_MODEL = "security-model"; + /* package private */ MediaFormat(@NonNull Map<String, Object> map) { mMap = map; } diff --git a/media/java/android/media/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig index c4b38c725ea4..b16580927fa6 100644 --- a/media/java/android/media/flags/projection.aconfig +++ b/media/java/android/media/flags/projection.aconfig @@ -1,4 +1,4 @@ -package: "com.android.media.flags" +package: "com.android.media.projection.flags" # Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes diff --git a/media/java/android/media/metrics/EditingEndedEvent.java b/media/java/android/media/metrics/EditingEndedEvent.java index 9b3477f4bd69..54496bff077f 100644 --- a/media/java/android/media/metrics/EditingEndedEvent.java +++ b/media/java/android/media/metrics/EditingEndedEvent.java @@ -199,7 +199,7 @@ public final class EditingEndedEvent extends Event implements Parcelable { /** Input audio was edited. */ public static final long OPERATION_TYPE_AUDIO_EDIT = 1L << 3; - /** Input video samples were writted (muxed) directly to the output file without transcoding. */ + /** Input video samples were written (muxed) directly to the output file without transcoding. */ public static final long OPERATION_TYPE_VIDEO_TRANSMUX = 1L << 4; /** Input audio samples were written (muxed) directly to the output file without transcoding. */ @@ -272,7 +272,8 @@ public final class EditingEndedEvent extends Event implements Parcelable { } /** - * Returns the name of the library implementing the exporting operation, or {@code null} if + * Returns the name of the library implementing the exporting operation, for example, a Maven + * artifact ID like "androidx.media3.media3-transformer:1.3.0-beta01", or {@code null} if * unknown. */ @Nullable @@ -281,8 +282,8 @@ public final class EditingEndedEvent extends Event implements Parcelable { } /** - * Returns the name of the library implementing the media muxing operation, or {@code null} if - * unknown. + * Returns the name of the library implementing the media muxing operation, for example, a Maven + * artifact ID like "androidx.media3.media3-muxer:1.3.0-beta01", or {@code null} if unknown. */ @Nullable public String getMuxerName() { diff --git a/media/java/android/media/metrics/MediaItemInfo.java b/media/java/android/media/metrics/MediaItemInfo.java index 63dd3ccd3b33..338697fef76b 100644 --- a/media/java/android/media/metrics/MediaItemInfo.java +++ b/media/java/android/media/metrics/MediaItemInfo.java @@ -92,7 +92,7 @@ public final class MediaItemInfo implements Parcelable { DATA_TYPE_DEPTH, DATA_TYPE_GAIN_MAP, DATA_TYPE_HIGH_FRAME_RATE, - DATA_TYPE_CUE_POINTS, + DATA_TYPE_SPEED_SETTING_CUE_POINTS, DATA_TYPE_GAPLESS, DATA_TYPE_SPATIAL_AUDIO, DATA_TYPE_HIGH_DYNAMIC_RANGE_VIDEO, @@ -109,7 +109,10 @@ public final class MediaItemInfo implements Parcelable { /** The media item includes audio data. */ public static final long DATA_TYPE_AUDIO = 1L << 2; - /** The media item includes metadata. */ + /** + * The media item includes static media container metadata (for example, capture frame rate or + * location information). + */ public static final long DATA_TYPE_METADATA = 1L << 3; /** The media item includes depth (z-distance) information. */ @@ -121,8 +124,11 @@ public final class MediaItemInfo implements Parcelable { /** The media item includes high frame rate video data. */ public static final long DATA_TYPE_HIGH_FRAME_RATE = 1L << 6; - /** The media item includes time-dependent speed setting metadata. */ - public static final long DATA_TYPE_CUE_POINTS = 1L << 7; + /** + * The media item includes time-dependent speed information (for example, slow motion cue + * points). + */ + public static final long DATA_TYPE_SPEED_SETTING_CUE_POINTS = 1L << 7; /** The media item includes gapless audio metadata. */ public static final long DATA_TYPE_GAPLESS = 1L << 8; diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index fa9afa872091..a8e94234e063 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -232,52 +232,8 @@ public abstract class MediaBrowserService extends Service { + " package=" + pkg); } - service.mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - // Clear out the old subscriptions. We are getting new ones. - service.mServiceState.mConnections.remove(b); - - // Temporarily sets a placeholder ConnectionRecord to make - // getCurrentBrowserInfo() work in onGetRoot(). - service.mServiceState.mCurConnection = - new ConnectionRecord( - service, pkg, pid, uid, rootHints, callbacks, null); - BrowserRoot root = service.onGetRoot(pkg, uid, rootHints); - service.mServiceState.mCurConnection = null; - - // If they didn't return something, don't allow this client. - if (root == null) { - Log.i(TAG, "No root for client " + pkg + " from service " - + getClass().getName()); - try { - callbacks.onConnectFailed(); - } catch (RemoteException ex) { - Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " - + "pkg=" + pkg); - } - } else { - try { - ConnectionRecord connection = - new ConnectionRecord( - service, pkg, pid, uid, rootHints, callbacks, root); - service.mServiceState.mConnections.put(b, connection); - b.linkToDeath(connection, 0); - if (service.mServiceState.mSession != null) { - callbacks.onConnect( - connection.root.getRootId(), - service.mServiceState.mSession, - connection.root.getExtras()); - } - } catch (RemoteException ex) { - Log.w(TAG, "Calling onConnect() failed. Dropping client. " - + "pkg=" + pkg); - service.mServiceState.mConnections.remove(b); - } - } - } - }); + service.mHandler.post( + () -> service.connectOnHandler(pkg, pid, uid, rootHints, callbacks)); } @Override @@ -315,22 +271,8 @@ public abstract class MediaBrowserService extends Service { return; } - service.mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - // Get the record for the connection - ConnectionRecord connection = service.mServiceState.mConnections.get(b); - if (connection == null) { - Log.w(TAG, "addSubscription for callback that isn't registered id=" - + id); - return; - } - - service.addSubscription(id, connection, token, options); - } - }); + service.mHandler.post( + () -> service.addSubscriptionOnHandler(id, callbacks, token, options)); } @Override @@ -347,23 +289,12 @@ public abstract class MediaBrowserService extends Service { return; } - service.mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - ConnectionRecord connection = service.mServiceState.mConnections.get(b); - if (connection == null) { - Log.w(TAG, "removeSubscription for callback that isn't registered id=" - + id); - return; - } - if (!service.removeSubscription(id, connection, token)) { - Log.w(TAG, "removeSubscription called for " + id - + " which is not subscribed"); - } - } - }); + service.mHandler.post( + () -> { + if (!service.removeSubscriptionOnHandler(id, callbacks, token)) { + Log.w(TAG, "removeSubscription for id with no subscription: " + id); + } + }); } @Override @@ -374,18 +305,8 @@ public abstract class MediaBrowserService extends Service { return; } - service.mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = service.mServiceState.mConnections.get(b); - if (connection == null) { - Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); - return; - } - service.performLoadItem(mediaId, connection, receiver); - } - }); + service.mHandler.post( + () -> service.performLoadItemOnHandler(mediaId, callbacks, receiver)); } } @@ -527,22 +448,7 @@ public abstract class MediaBrowserService extends Service { throw new IllegalStateException("The session token has already been set."); } mServiceState.mSession = token; - mHandler.post(new Runnable() { - @Override - public void run() { - Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator(); - while (iter.hasNext()) { - ConnectionRecord connection = iter.next(); - try { - connection.callbacks.onConnect(connection.root.getRootId(), token, - connection.root.getExtras()); - } catch (RemoteException e) { - Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); - iter.remove(); - } - } - } - }); + mHandler.post(() -> notifySessionTokenInitializedOnHandler(token)); } /** @@ -599,7 +505,7 @@ public abstract class MediaBrowserService extends Service { * children changed. */ public void notifyChildrenChanged(@NonNull String parentId) { - notifyChildrenChangedInternal(parentId, null); + notifyChildrenChanged(parentId, Bundle.EMPTY); } /** @@ -617,30 +523,10 @@ public abstract class MediaBrowserService extends Service { if (options == null) { throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged"); } - notifyChildrenChangedInternal(parentId, options); - } - - private void notifyChildrenChangedInternal(final String parentId, final Bundle options) { if (parentId == null) { throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); } - mHandler.post(new Runnable() { - @Override - public void run() { - for (IBinder binder : mServiceState.mConnections.keySet()) { - ConnectionRecord connection = mServiceState.mConnections.get(binder); - List<Pair<IBinder, Bundle>> callbackList = - connection.subscriptions.get(parentId); - if (callbackList != null) { - for (Pair<IBinder, Bundle> callback : callbackList) { - if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) { - performLoadChildren(parentId, connection, callback.second); - } - } - } - } - } - }); + mHandler.post(() -> notifyChildrenChangeOnHandler(parentId, options)); } /** @@ -661,11 +547,45 @@ public abstract class MediaBrowserService extends Service { return false; } - /** - * Save the subscription and if it is a new subscription send the results. - */ - private void addSubscription(String id, ConnectionRecord connection, IBinder token, - Bundle options) { + private void notifySessionTokenInitializedOnHandler(MediaSession.Token token) { + Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator(); + while (iter.hasNext()) { + ConnectionRecord connection = iter.next(); + try { + connection.callbacks.onConnect( + connection.root.getRootId(), token, connection.root.getExtras()); + } catch (RemoteException e) { + Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); + iter.remove(); + } + } + } + + private void notifyChildrenChangeOnHandler(final String parentId, final Bundle options) { + for (IBinder binder : mServiceState.mConnections.keySet()) { + ConnectionRecord connection = mServiceState.mConnections.get(binder); + List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId); + if (callbackList != null) { + for (Pair<IBinder, Bundle> callback : callbackList) { + if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) { + performLoadChildrenOnHandler(parentId, connection, callback.second); + } + } + } + } + } + + /** Save the subscription and if it is a new subscription send the results. */ + private void addSubscriptionOnHandler( + String id, IMediaBrowserServiceCallbacks callbacks, IBinder token, Bundle options) { + IBinder b = callbacks.asBinder(); + // Get the record for the connection + ConnectionRecord connection = mServiceState.mConnections.get(b); + if (connection == null) { + Log.w(TAG, "addSubscription for callback that isn't registered id=" + id); + return; + } + // Save the subscription List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id); if (callbackList == null) { @@ -680,13 +600,66 @@ public abstract class MediaBrowserService extends Service { callbackList.add(new Pair<>(token, options)); connection.subscriptions.put(id, callbackList); // send the results - performLoadChildren(id, connection, options); + performLoadChildrenOnHandler(id, connection, options); } - /** - * Remove the subscription. - */ - private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) { + private void connectOnHandler( + String pkg, + int pid, + int uid, + Bundle rootHints, + IMediaBrowserServiceCallbacks callbacks) { + IBinder b = callbacks.asBinder(); + // Clear out the old subscriptions. We are getting new ones. + mServiceState.mConnections.remove(b); + + // Temporarily sets a placeholder ConnectionRecord to make getCurrentBrowserInfo() work in + // onGetRoot(). + mServiceState.mCurConnection = + new ConnectionRecord( + /* service= */ this, pkg, pid, uid, rootHints, callbacks, /* root= */ null); + BrowserRoot root = onGetRoot(pkg, uid, rootHints); + mServiceState.mCurConnection = null; + + // If they didn't return something, don't allow this client. + if (root == null) { + Log.i(TAG, "No root for client " + pkg + " from service " + getClass().getName()); + try { + callbacks.onConnectFailed(); + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. pkg=" + pkg); + } + } else { + try { + ConnectionRecord connection = + new ConnectionRecord( + /* service= */ this, pkg, pid, uid, rootHints, callbacks, root); + mServiceState.mConnections.put(b, connection); + b.linkToDeath(connection, /* flags= */ 0); + if (mServiceState.mSession != null) { + callbacks.onConnect( + connection.root.getRootId(), + mServiceState.mSession, + connection.root.getExtras()); + } + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnect() failed. Dropping client. pkg=" + pkg); + mServiceState.mConnections.remove(b); + } + } + } + + /** Remove the subscription. */ + private boolean removeSubscriptionOnHandler( + String id, IMediaBrowserServiceCallbacks callbacks, IBinder token) { + final IBinder b = callbacks.asBinder(); + + ConnectionRecord connection = mServiceState.mConnections.get(b); + if (connection == null) { + Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id); + return true; + } + if (token == null) { return connection.subscriptions.remove(id) != null; } @@ -700,7 +673,7 @@ public abstract class MediaBrowserService extends Service { iter.remove(); } } - if (callbackList.size() == 0) { + if (callbackList.isEmpty()) { connection.subscriptions.remove(id); } } @@ -709,44 +682,53 @@ public abstract class MediaBrowserService extends Service { /** * Call onLoadChildren and then send the results back to the connection. - * <p> - * Callers must make sure that this connection is still connected. + * + * <p>Callers must make sure that this connection is still connected. */ - private void performLoadChildren(final String parentId, final ConnectionRecord connection, - final Bundle options) { + private void performLoadChildrenOnHandler( + final String parentId, final ConnectionRecord connection, final Bundle options) { final Result<List<MediaBrowser.MediaItem>> result = - new Result<List<MediaBrowser.MediaItem>>(parentId) { - @Override - void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { - if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) { - if (DBG) { - Log.d(TAG, "Not sending onLoadChildren result for connection that has" - + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); - } - return; - } + new Result<>(parentId) { + @Override + void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { + if (mServiceState.mConnections.get(connection.callbacks.asBinder()) + != connection) { + if (DBG) { + Log.d( + TAG, + "Not sending onLoadChildren result for connection that has" + + " been disconnected. pkg=" + + connection.pkg + + " id=" + + parentId); + } + return; + } - List<MediaBrowser.MediaItem> filteredList = - (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 - ? MediaBrowserUtils.applyPagingOptions(list, options) : list; - final ParceledListSlice<MediaBrowser.MediaItem> pls; - if (filteredList == null) { - pls = null; - } else { - pls = new ParceledListSlice<>(filteredList); - // Limit the size of initial Parcel to prevent binder buffer overflow - // as onLoadChildren is an async binder call. - pls.setInlineCountLimit(1); - } - try { - connection.callbacks.onLoadChildren(parentId, pls, options); - } catch (RemoteException ex) { - // The other side is in the process of crashing. - Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId - + " package=" + connection.pkg); - } - } - }; + List<MediaBrowser.MediaItem> filteredList = + (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 + ? MediaBrowserUtils.applyPagingOptions(list, options) + : list; + ParceledListSlice<MediaBrowser.MediaItem> pls = null; + if (filteredList != null) { + pls = new ParceledListSlice<>(filteredList); + // Limit the size of initial Parcel to prevent binder buffer overflow + // as onLoadChildren is an async binder call. + pls.setInlineCountLimit(1); + } + try { + connection.callbacks.onLoadChildren(parentId, pls, options); + } catch (RemoteException ex) { + // The other side is in the process of crashing. + Log.w( + TAG, + "Calling onLoadChildren() failed for id=" + + parentId + + " package=" + + connection.pkg); + } + } + }; mServiceState.mCurConnection = connection; if (options == null) { @@ -762,28 +744,41 @@ public abstract class MediaBrowserService extends Service { } } - private void performLoadItem(String itemId, final ConnectionRecord connection, - final ResultReceiver receiver) { + private void performLoadItemOnHandler( + String itemId, IMediaBrowserServiceCallbacks callbacks, final ResultReceiver receiver) { + final IBinder b = callbacks.asBinder(); + ConnectionRecord connection = mServiceState.mConnections.get(b); + if (connection == null) { + Log.w(TAG, "getMediaItem for callback that isn't registered id=" + itemId); + return; + } + final Result<MediaBrowser.MediaItem> result = - new Result<MediaBrowser.MediaItem>(itemId) { - @Override - void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { - if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) { - if (DBG) { - Log.d(TAG, "Not sending onLoadItem result for connection that has" - + " been disconnected. pkg=" + connection.pkg + " id=" + itemId); + new Result<>(itemId) { + @Override + void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { + if (mServiceState.mConnections.get(connection.callbacks.asBinder()) + != connection) { + if (DBG) { + Log.d( + TAG, + "Not sending onLoadItem result for connection that has" + + " been disconnected. pkg=" + + connection.pkg + + " id=" + + itemId); + } + return; + } + if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) { + receiver.send(RESULT_ERROR, null); + return; + } + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_MEDIA_ITEM, item); + receiver.send(RESULT_OK, bundle); } - return; - } - if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) { - receiver.send(RESULT_ERROR, null); - return; - } - Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_MEDIA_ITEM, item); - receiver.send(RESULT_OK, bundle); - } - }; + }; mServiceState.mCurConnection = connection; onLoadItem(itemId, result); diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index 3254a394c25a..f264b16347f9 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -44,6 +44,8 @@ import android.util.Log; import android.util.Xml; import android.util.proto.ProtoOutputStream; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -138,6 +140,11 @@ public final class ApduServiceInfo implements Parcelable { private boolean mCategoryOtherServiceEnabled; /** + * Whether the NFC stack should default to Observe Mode when this preferred service. + */ + private boolean mDefaultToObserveMode; + + /** * @hide */ @UnsupportedAppUsage @@ -257,6 +264,9 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.HostApduService_settingsActivity); mOffHostName = null; mStaticOffHostName = mOffHostName; + mDefaultToObserveMode = sa.getBoolean( + R.styleable.HostApduService_defaultToObserveMode, + false); sa.recycle(); } else { TypedArray sa = res.obtainAttributes(attrs, @@ -276,6 +286,9 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.HostApduService_settingsActivity); mOffHostName = sa.getString( com.android.internal.R.styleable.OffHostApduService_secureElementName); + mDefaultToObserveMode = sa.getBoolean( + R.styleable.HostApduService_defaultToObserveMode, + false); if (mOffHostName != null) { if (mOffHostName.equals("eSE")) { mOffHostName = "eSE1"; @@ -611,6 +624,25 @@ public final class ApduServiceInfo implements Parcelable { } /** + * Returns whether the NFC stack should default to observe mode when this servise is preferred. + * @return whether the NFC stack should default to observe mode when this servise is preferred + */ + @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) + public boolean defaultToObserveMode() { + return mDefaultToObserveMode; + } + + /** + * Sets whether the NFC stack should default to observe mode when this servise is preferred. + * @param defaultToObserveMode whether the NFC stack should default to observe mode when this + * servise is preferred + */ + @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) + public void setDefaultToObserveMode(boolean defaultToObserveMode) { + mDefaultToObserveMode = defaultToObserveMode; + } + + /** * Returns description of service. * @return user readable description of service */ diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java index 6766afc5e45a..b5951e8e7927 100644 --- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java +++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java @@ -40,7 +40,6 @@ import android.provider.Settings; import android.sysprop.CrashRecoveryProperties; import android.text.TextUtils; import android.util.ArraySet; -import android.util.ExceptionUtils; import android.util.Log; import android.util.Slog; @@ -136,7 +135,7 @@ public class RescueParty { } // We're disabled on all engineering devices - if (Build.IS_ENG) { + if (Build.TYPE.equals("eng")) { Slog.v(TAG, "Disabled because of eng build"); return true; } @@ -144,7 +143,7 @@ public class RescueParty { // We're disabled on userdebug devices connected over USB, since that's // a decent signal that someone is actively trying to debug the device, // or that it's in a lab environment. - if (Build.IS_USERDEBUG && isUsbActive()) { + if (Build.TYPE.equals("userdebug") && isUsbActive()) { Slog.v(TAG, "Disabled because of active USB connection"); return true; } @@ -478,9 +477,18 @@ public class RescueParty { } } + private static String getCompleteMessage(Throwable t) { + final StringBuilder builder = new StringBuilder(); + builder.append(t.getMessage()); + while ((t = t.getCause()) != null) { + builder.append(": ").append(t.getMessage()); + } + return builder.toString(); + } + private static void logRescueException(int level, @Nullable String failedPackageName, Throwable t) { - final String msg = ExceptionUtils.getCompleteMessage(t); + final String msg = getCompleteMessage(t); EventLogTags.writeRescueFailure(level, msg); String failureMsg = "Failed rescue level " + levelToString(level); if (!TextUtils.isEmpty(failedPackageName)) { diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml index 4f5196d017e6..79fe5ce096a6 100644 --- a/packages/CredentialManager/res/values-kk/strings.xml +++ b/packages/CredentialManager/res/values-kk/strings.xml @@ -92,8 +92,6 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы сұрауды тоқтатты."</string> - <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> - <skip /> - <!-- no translation found for more_options_content_description (1323427365788198808) --> - <skip /> + <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Кіру опциялары"</string> + <string name="more_options_content_description" msgid="1323427365788198808">"Жаю"</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt b/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt new file mode 100644 index 000000000000..cda6b8bb36be --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.media.session + +import android.media.session.MediaController +import android.media.session.MediaSessionManager +import android.os.UserHandle +import androidx.concurrent.futures.DirectExecutor +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch + +/** [Flow] for [MediaSessionManager.OnActiveSessionsChangedListener]. */ +val MediaSessionManager.activeMediaChanges: Flow<Collection<MediaController>?> + get() = + callbackFlow { + val listener = + MediaSessionManager.OnActiveSessionsChangedListener { launch { send(it) } } + addOnActiveSessionsChangedListener( + null, + UserHandle.of(UserHandle.myUserId()), + DirectExecutor.INSTANCE, + listener, + ) + awaitClose { removeOnActiveSessionsChangedListener(listener) } + } + .buffer(capacity = Channel.CONFLATED) diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt new file mode 100644 index 000000000000..1f037c0280e3 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.volume.data.repository + +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.os.Bundle +import android.os.Handler +import kotlinx.coroutines.channels.ProducerScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch + +/** [MediaController.Callback] flow representation. */ +fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> { + return callbackFlow { + val callback = MediaControllerCallbackProducer(this) + registerCallback(callback, handler) + awaitClose { unregisterCallback(callback) } + } +} + +/** Models particular change event received by [MediaController.Callback]. */ +sealed interface MediaControllerChange { + + data object SessionDestroyed : MediaControllerChange + + data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange + + data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange + + data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange + + data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) : + MediaControllerChange + + data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange + + data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange + + data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange +} + +private class MediaControllerCallbackProducer( + private val producingScope: ProducerScope<MediaControllerChange> +) : MediaController.Callback() { + + override fun onSessionDestroyed() { + send(MediaControllerChange.SessionDestroyed) + } + + override fun onSessionEvent(event: String, extras: Bundle?) { + send(MediaControllerChange.SessionEvent(event, extras)) + } + + override fun onPlaybackStateChanged(state: PlaybackState?) { + send(MediaControllerChange.PlaybackStateChanged(state)) + } + + override fun onMetadataChanged(metadata: MediaMetadata?) { + send(MediaControllerChange.MetadataChanged(metadata)) + } + + override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) { + send(MediaControllerChange.QueueChanged(queue)) + } + + override fun onQueueTitleChanged(title: CharSequence?) { + send(MediaControllerChange.QueueTitleChanged(title)) + } + + override fun onExtrasChanged(extras: Bundle?) { + send(MediaControllerChange.ExtrasChanged(extras)) + } + + override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) { + send(MediaControllerChange.AudioInfoChanged(info)) + } + + private fun send(change: MediaControllerChange) { + producingScope.launch { producingScope.send(change) } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt index ab8c6b820177..6925c71fc68f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt @@ -16,21 +16,23 @@ package com.android.settingslib.volume.data.repository +import android.content.Intent import android.media.AudioManager import android.media.session.MediaController import android.media.session.MediaSessionManager import android.media.session.PlaybackState import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.bluetooth.headsetAudioModeChanges +import com.android.settingslib.media.session.activeMediaChanges import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn @@ -38,7 +40,7 @@ import kotlinx.coroutines.flow.stateIn interface MediaControllerRepository { /** Current [MediaController]. Null is emitted when there is no active [MediaController]. */ - val activeMediaController: StateFlow<MediaController?> + val activeLocalMediaController: StateFlow<MediaController?> } class MediaControllerRepositoryImpl( @@ -53,26 +55,28 @@ class MediaControllerRepositoryImpl( audioManagerIntentsReceiver.intents.filter { AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action } - override val activeMediaController: StateFlow<MediaController?> = - buildList { - localBluetoothManager?.headsetAudioModeChanges?.let { add(it) } - add(devicesChanges) + + override val activeLocalMediaController: StateFlow<MediaController?> = + combine( + mediaSessionManager.activeMediaChanges.onStart { + emit(mediaSessionManager.getActiveSessions(null)) + }, + localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) } + ?: flowOf(null), + devicesChanges.onStart { emit(Intent()) }, + ) { controllers, _, _ -> + controllers?.let(::findLocalMediaController) } - .merge() - .onStart { emit(Unit) } - .map { getActiveLocalMediaController() } .flowOn(backgroundContext) .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null) - private fun getActiveLocalMediaController(): MediaController? { + private fun findLocalMediaController( + controllers: Collection<MediaController>, + ): MediaController? { var localController: MediaController? = null val remoteMediaSessionLists: MutableList<String> = ArrayList() - for (controller in mediaSessionManager.getActiveSessions(null)) { + for (controller in controllers) { val playbackInfo: MediaController.PlaybackInfo = controller.playbackInfo ?: continue - val playbackState = controller.playbackState ?: continue - if (inactivePlaybackStates.contains(playbackState.state)) { - continue - } when (playbackInfo.playbackType) { MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE -> { if (localController?.packageName.equals(controller.packageName)) { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt index 430d733e4a88..7bd43d2cf8ab 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt @@ -116,7 +116,7 @@ class MediaControllerRepositoryImplTest { ) ) var mediaController: MediaController? = null - underTest.activeMediaController + underTest.activeLocalMediaController .onEach { mediaController = it } .launchIn(backgroundScope) runCurrent() @@ -141,7 +141,7 @@ class MediaControllerRepositoryImplTest { ) ) var mediaController: MediaController? = null - underTest.activeMediaController + underTest.activeLocalMediaController .onEach { mediaController = it } .launchIn(backgroundScope) runCurrent() diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java index 5669276a0424..8edda1a1f3a2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java @@ -90,7 +90,7 @@ public class DeviceIconUtilTest { public void getIconResIdFromMediaRouteType_hdmi() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test @@ -101,10 +101,10 @@ public class DeviceIconUtilTest { } @Test - public void getIconResIdFromMediaRouteType_hdmiArc_isHeadphone() { + public void getIconResIdFromMediaRouteType_hdmiArc_isExternalDisplay() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test @@ -115,10 +115,10 @@ public class DeviceIconUtilTest { } @Test - public void getIconResIdFromMediaRouteType_hdmiEarc_isHeadphone() { + public void getIconResIdFromMediaRouteType_hdmiEarc_isExternalDisplay() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test @@ -229,10 +229,10 @@ public class DeviceIconUtilTest { } @Test - public void getIconResIdFromAudioDeviceType_hdmi_isHeadphone() { + public void getIconResIdFromAudioDeviceType_hdmi_isExternalDisplay() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test @@ -243,10 +243,10 @@ public class DeviceIconUtilTest { } @Test - public void getIconResIdFromAudioDeviceType_hdmiArc_isHeadphone() { + public void getIconResIdFromAudioDeviceType_hdmiArc_isExternalDisplay() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test @@ -257,10 +257,10 @@ public class DeviceIconUtilTest { } @Test - public void getIconResIdFromAudioDeviceType_hdmiEarc_isHeadphone() { + public void getIconResIdFromAudioDeviceType_hdmiEarc_isExternalDisplay() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC)) - .isEqualTo(R.drawable.ic_headphone); + .isEqualTo(R.drawable.ic_external_display); } @Test diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 4305d912e806..53f2caf0b793 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -771,6 +771,9 @@ <!-- Permission required for CTS test - CtsDevicePolicyManagerTestCases --> <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" /> + <!-- Permission required for CTS test - CtsDevicePolicyTestCases --> + <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" /> + <!-- Permission required for CTS test - CtsKeystoreTestCases --> <uses-permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" /> diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt new file mode 100644 index 000000000000..aad593eb6a05 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.surfaceeffects.loadingeffect + +import android.content.Context +import android.graphics.BlendMode +import android.graphics.Canvas +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +/** Custom View for drawing the [LoadingEffect] with [Canvas.drawPaint]. */ +open class LoadingEffectView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { + + private var paint: Paint? = null + private var blendMode: BlendMode = BlendMode.SRC_OVER + + override fun onDraw(canvas: Canvas) { + if (!canvas.isHardwareAccelerated) { + return + } + paint?.let { canvas.drawPaint(it) } + } + + /** Designed to be called on [LoadingEffect.PaintDrawCallback.onDraw]. */ + fun draw(paint: Paint) { + this.paint = paint + this.paint!!.blendMode = blendMode + + invalidate() + } + + /** Sets the blend mode of the [Paint]. */ + fun setBlendMode(blendMode: BlendMode) { + this.blendMode = blendMode + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt index 1d6f813cfbdf..b28655b28b65 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt @@ -69,6 +69,7 @@ class AndroidColorScheme(context: Context) { val onTertiary = getColor(context, R.attr.materialColorOnTertiary) val surfaceDim = getColor(context, R.attr.materialColorSurfaceDim) val surfaceBright = getColor(context, R.attr.materialColorSurfaceBright) + val error = getColor(context, R.attr.materialColorError) val onError = getColor(context, R.attr.materialColorOnError) val surface = getColor(context, R.attr.materialColorSurface) val surfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh) 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 374a97d769c8..4398b2541f65 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 @@ -69,7 +69,7 @@ object ComposeFacade : BaseComposeFacade { override fun setVolumePanelActivityContent( activity: ComponentActivity, viewModel: VolumePanelViewModel, - onDismissAnimationFinished: () -> Unit, + onDismiss: () -> Unit, ) { throwComposeUnavailableError() } diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt new file mode 100644 index 000000000000..8ad0a080a9dd --- /dev/null +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput + +import dagger.Module + +@Module interface MediaOutputModule 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 a1bbc7d7bfb8..aa567364d130 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 @@ -102,12 +102,12 @@ object ComposeFacade : BaseComposeFacade { override fun setVolumePanelActivityContent( activity: ComponentActivity, viewModel: VolumePanelViewModel, - onDismissAnimationFinished: () -> Unit, + onDismiss: () -> Unit, ) { activity.setContent { VolumePanelRoot( viewModel = viewModel, - onDismissAnimationFinished = onDismissAnimationFinished, + onDismiss = onDismiss, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt index d9493961e3ed..621ddf796f58 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt @@ -557,13 +557,18 @@ private fun StatusMessage( targetState = message, label = "Bouncer message", animationSpec = if (message.isUpdateAnimated) tween() else snap(), - modifier = modifier, + modifier = modifier.fillMaxWidth(), ) { - Text( - text = it.text, - color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.bodyLarge, - ) + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = it.text, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyLarge, + ) + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt index f387021daeac..ed2cbb85ba1a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt @@ -21,10 +21,10 @@ import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.notifications.ui.composable.NotificationStack import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.statusbar.notification.stack.AmbientState @@ -59,37 +59,38 @@ constructor( ) { init { - if (!KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) { - // This scene container section moves the NSSL to the SharedNotificationContainer. - // This also requires that SharedNotificationContainer gets moved to the - // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container, - // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this - // container by the NotificationStackScrollLayoutSection. - // Ensure stackScrollLayout is a child of sharedNotificationContainer. + if (!migrateClocksToBlueprint()) { + throw IllegalStateException("this requires migrateClocksToBlueprint()") + } + // This scene container section moves the NSSL to the SharedNotificationContainer. + // This also requires that SharedNotificationContainer gets moved to the + // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container, + // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this + // container by the NotificationStackScrollLayoutSection. + // Ensure stackScrollLayout is a child of sharedNotificationContainer. - if (stackScrollLayout.parent != sharedNotificationContainer) { - (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout) - sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout) - } + if (stackScrollLayout.parent != sharedNotificationContainer) { + (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout) + sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout) + } - SharedNotificationContainerBinder.bind( + SharedNotificationContainerBinder.bind( + sharedNotificationContainer, + sharedNotificationContainerViewModel, + sceneContainerFlags, + controller, + notificationStackSizeCalculator, + mainDispatcher, + ) + + if (sceneContainerFlags.flexiNotifsEnabled()) { + NotificationStackAppearanceViewBinder.bind( + context, sharedNotificationContainer, - sharedNotificationContainerViewModel, - sceneContainerFlags, + notificationStackAppearanceViewModel, + ambientState, controller, - notificationStackSizeCalculator, - mainDispatcher, ) - - if (sceneContainerFlags.flexiNotifsEnabled()) { - NotificationStackAppearanceViewBinder.bind( - context, - sharedNotificationContainer, - notificationStackAppearanceViewModel, - ambientState, - controller, - ) - } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt index 43d545368536..236aee217f16 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt @@ -32,7 +32,7 @@ interface BottomBarModule { @Binds @IntoMap @StringKey(VolumePanelComponents.BOTTOM_BAR) - fun bindMediaVolumeSliderComponent(component: BottomBarComponent): VolumePanelUiComponent + fun bindVolumePanelUiComponent(component: BottomBarComponent): VolumePanelUiComponent @Binds @IntoMap diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt index 03c07f714541..0cf43672c716 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt @@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -49,7 +51,13 @@ constructor( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - PlatformOutlinedButton(onClick = viewModel::onSettingsClicked) { + PlatformOutlinedButton( + onClick = viewModel::onSettingsClicked, + colors = + ButtonDefaults.outlinedButtonColors( + contentColor = MaterialTheme.colorScheme.onSurface, + ), + ) { Text(text = stringResource(R.string.volume_panel_dialog_settings_button)) } PlatformButton(onClick = viewModel::onDoneClicked) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt new file mode 100644 index 000000000000..c73656eb1ec5 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput + +import com.android.systemui.volume.panel.component.mediaoutput.domain.MediaOutputAvailabilityCriteria +import com.android.systemui.volume.panel.component.mediaoutput.ui.composable.MediaOutputComponent +import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents +import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria +import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey + +/** Dagger module, that provides media output Volume Panel UI functionality. */ +@Module +interface MediaOutputModule { + + @Binds + @IntoMap + @StringKey(VolumePanelComponents.MEDIA_OUTPUT) + fun bindVolumePanelUiComponent(component: MediaOutputComponent): VolumePanelUiComponent + + @Binds + @IntoMap + @StringKey(VolumePanelComponents.MEDIA_OUTPUT) + fun bindComponentAvailabilityCriteria( + criteria: MediaOutputAvailabilityCriteria + ): ComponentAvailabilityCriteria +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt new file mode 100644 index 000000000000..8ad6fdf829d7 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.ui.composable + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.snap +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.aspectRatio +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.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.android.compose.animation.Expandable +import com.android.systemui.common.ui.compose.Icon +import com.android.systemui.common.ui.compose.toColor +import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.ConnectedDeviceViewModel +import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.DeviceIconViewModel +import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.MediaOutputViewModel +import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent +import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope +import javax.inject.Inject + +@VolumePanelScope +class MediaOutputComponent +@Inject +constructor( + private val viewModel: MediaOutputViewModel, +) : ComposeVolumePanelUiComponent { + + @Composable + override fun VolumePanelComposeScope.Content(modifier: Modifier) { + val connectedDeviceViewModel: ConnectedDeviceViewModel? by + viewModel.connectedDeviceViewModel.collectAsState() + val deviceIconViewModel: DeviceIconViewModel? by + viewModel.deviceIconViewModel.collectAsState() + + Expandable( + modifier = Modifier.fillMaxWidth().height(80.dp), + color = MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(28.dp), + onClick = { viewModel.onBarClick(it) }, + ) { + Row { + connectedDeviceViewModel?.let { ConnectedDeviceText(it) } + + deviceIconViewModel?.let { ConnectedDeviceIcon(it) } + } + } + } + + @Composable + private fun RowScope.ConnectedDeviceText(connectedDeviceViewModel: ConnectedDeviceViewModel) { + Column( + modifier = + Modifier.weight(1f) + .padding(start = 24.dp, top = 20.dp, bottom = 20.dp) + .fillMaxHeight(), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + connectedDeviceViewModel.label.toString(), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + connectedDeviceViewModel.deviceName?.let { + Text( + it.toString(), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + } + + @Composable + private fun ConnectedDeviceIcon(deviceIconViewModel: DeviceIconViewModel) { + val transition = updateTransition(deviceIconViewModel, label = "MediaOutputIconTransition") + Box( + modifier = Modifier.padding(16.dp).fillMaxHeight().aspectRatio(1f), + contentAlignment = Alignment.Center + ) { + transition.AnimatedContent( + contentKey = { it.backgroundColor }, + transitionSpec = { + if (targetState is DeviceIconViewModel.IsPlaying) { + scaleIn( + initialScale = 0.9f, + animationSpec = isPlayingInIconBackgroundSpec(), + ) + fadeIn(animationSpec = isPlayingInIconBackgroundSpec()) togetherWith + fadeOut(animationSpec = snap()) + } else { + fadeIn(animationSpec = snap(delayMillis = 900)) togetherWith + scaleOut( + targetScale = 0.9f, + animationSpec = isPlayingOutSpec(), + ) + fadeOut(animationSpec = isPlayingOutSpec()) + } + } + ) { targetViewModel -> + Expandable( + modifier = Modifier.fillMaxSize(), + color = targetViewModel.backgroundColor.toColor(), + shape = RoundedCornerShape(12.dp), + onClick = { viewModel.onDeviceClick(it) }, + ) {} + } + transition.AnimatedContent( + contentKey = { it.icon }, + transitionSpec = { + if (targetState is DeviceIconViewModel.IsPlaying) { + fadeIn(animationSpec = snap(delayMillis = 700)) togetherWith + slideOutVertically( + targetOffsetY = { it }, + animationSpec = isPlayingInIconSpec(), + ) + fadeOut(animationSpec = isNotPlayingOutIconSpec()) + } else { + slideInVertically( + initialOffsetY = { it }, + animationSpec = isNotPlayingInIconSpec(), + ) + fadeIn(animationSpec = isNotPlayingInIconSpec()) togetherWith + fadeOut(animationSpec = isPlayingOutSpec()) + } + } + ) { + Icon( + icon = it.icon, + modifier = Modifier.padding(12.dp).fillMaxSize(), + ) + } + } + } +} + +private fun <T> isPlayingOutSpec() = tween<T>(durationMillis = 400, delayMillis = 500) + +private fun <T> isPlayingInIconSpec() = tween<T>(durationMillis = 400, delayMillis = 300) + +private fun <T> isPlayingInIconBackgroundSpec() = tween<T>(durationMillis = 400, delayMillis = 700) + +private fun <T> isNotPlayingOutIconSpec() = tween<T>(durationMillis = 400, delayMillis = 300) + +private fun <T> isNotPlayingInIconSpec() = tween<T>(durationMillis = 400, delayMillis = 900) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt new file mode 100644 index 000000000000..98ef0674e8a1 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.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.volume.panel.ui.composable + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.systemui.volume.panel.ui.layout.ComponentsLayout + +@Composable +fun VolumePanelComposeScope.HorizontalVolumePanelContent( + layout: ComponentsLayout, + modifier: Modifier = Modifier, +) { + val spacing = 20.dp + Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(space = spacing)) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(spacing) + ) { + for (component in layout.contentComponents) { + AnimatedVisibility(component.isVisible) { + with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) } + } + } + } + + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(space = spacing, alignment = Alignment.Top) + ) { + for (component in layout.headerComponents) { + AnimatedVisibility(component.isVisible) { + with(component.component as ComposeVolumePanelUiComponent) { + Content(Modifier.weight(1f)) + } + } + } + Row( + modifier = Modifier.fillMaxWidth().wrapContentHeight(), + horizontalArrangement = Arrangement.spacedBy(spacing), + ) { + for (component in layout.footerComponents) { + AnimatedVisibility(component.isVisible) { + with(component.component as ComposeVolumePanelUiComponent) { + Content(Modifier.weight(1f)) + } + } + } + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt index e8d59660cebf..86eb84929c02 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.ui.composable import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -33,9 +34,16 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent( modifier: Modifier = Modifier, ) { Column( - modifier = modifier, + modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.spacedBy(20.dp), ) { + for (component in layout.headerComponents) { + AnimatedVisibility(component.isVisible) { + with(component.component as ComposeVolumePanelUiComponent) { + Content(Modifier.weight(1f)) + } + } + } for (component in layout.contentComponents) { AnimatedVisibility(component.isVisible) { with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) } @@ -44,11 +52,13 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent( if (layout.footerComponents.isNotEmpty()) { Row( modifier = Modifier.fillMaxWidth().wrapContentHeight(), - horizontalArrangement = Arrangement.spacedBy(20.dp) + horizontalArrangement = Arrangement.spacedBy(20.dp), ) { for (component in layout.footerComponents) { - with(component.component as ComposeVolumePanelUiComponent) { - Content(Modifier.weight(1f)) + AnimatedVisibility(component.isVisible) { + with(component.component as ComposeVolumePanelUiComponent) { + Content(Modifier.weight(1f)) + } } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt index c70c6b1ad861..10731c7f2df7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt @@ -16,17 +16,21 @@ package com.android.systemui.volume.panel.ui.composable +import android.content.res.Configuration import android.content.res.Configuration.Orientation import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState class VolumePanelComposeScope(private val state: VolumePanelState) { - /** - * Layout orientation of the panel. It doesn't necessarily aligns with the device orientation, - * because in some cases we want to show bigger version of a portrait orientation when the - * device is in landscape. - */ + /** Layout orientation of the panel. This aligns with the device orientation. */ @Orientation val orientation: Int get() = state.orientation + + /** Is true when Volume Panel is using wide-screen layout and false the otherwise. */ + val isWideScreen: Boolean + get() = state.isWideScreen } + +val VolumePanelComposeScope.isPortrait: Boolean + get() = orientation == Configuration.ORIENTATION_PORTRAIT diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt index 60d03fc6a107..dd6342029885 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt @@ -17,17 +17,13 @@ package com.android.systemui.volume.panel.ui.composable import android.content.res.Configuration -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.MutableTransitionState -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -37,11 +33,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.dp import com.android.compose.theme.PlatformTheme import com.android.systemui.res.R import com.android.systemui.volume.panel.ui.layout.ComponentsLayout @@ -51,48 +46,45 @@ import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel @Composable fun VolumePanelRoot( viewModel: VolumePanelViewModel, - onDismissAnimationFinished: () -> Unit, modifier: Modifier = Modifier, + onDismiss: () -> Unit ) { + LaunchedEffect(viewModel) { + viewModel.volumePanelState.collect { + if (!it.isVisible) { + onDismiss() + } + } + } + PlatformTheme(isSystemInDarkTheme()) { val state: VolumePanelState by viewModel.volumePanelState.collectAsState() val components by viewModel.componentsLayout.collectAsState(null) - val transitionState = - remember { MutableTransitionState(false) }.apply { targetState = state.isVisible } - - LaunchedEffect(transitionState.targetState, transitionState.isIdle) { - if (!transitionState.targetState && transitionState.isIdle) { - onDismissAnimationFinished() + with(VolumePanelComposeScope(state)) { + var boxModifier = modifier.fillMaxSize().clickable(onClick = onDismiss) + if (!isPortrait) { + boxModifier = boxModifier.padding(horizontal = 48.dp) } - } - - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.BottomCenter, - ) { - Spacer( - modifier = - Modifier.fillMaxSize() - .alpha(0.32f) - .background(MaterialTheme.colorScheme.scrim) - .clickable(onClick = { viewModel.dismissPanel() }) - ) - AnimatedVisibility( - visibleState = transitionState, - enter = slideInVertically { it }, - exit = slideOutVertically { it }, + Box( + modifier = boxModifier, + contentAlignment = Alignment.BottomCenter, ) { val radius = dimensionResource(R.dimen.volume_panel_corner_radius) Surface( + modifier = + Modifier.clickable( + interactionSource = null, + indication = null, + onClick = { + // prevent windowCloseOnTouchOutside from dismissing when tapped on + // the panel itself. + }, + ), shape = RoundedCornerShape(topStart = radius, topEnd = radius), color = MaterialTheme.colorScheme.surfaceContainer, ) { - Column { - components?.let { componentsState -> - with(VolumePanelComposeScope(state)) { Components(componentsState) } - } - } + Column { components?.let { componentsState -> Components(componentsState) } } } } } @@ -100,27 +92,38 @@ fun VolumePanelRoot( } @Composable -private fun VolumePanelComposeScope.Components(state: ComponentsLayout) { +private fun VolumePanelComposeScope.Components(components: ComponentsLayout) { if (orientation == Configuration.ORIENTATION_PORTRAIT) { VerticalVolumePanelContent( - state, - modifier = Modifier.padding(dimensionResource(R.dimen.volume_panel_content_padding)), + components, + modifier = Modifier.padding(24.dp), ) } else { - TODO("Add landscape layout") + HorizontalVolumePanelContent( + components, + modifier = + Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 20.dp) + .heightIn(max = 236.dp), + ) } - val horizontalPadding = dimensionResource(R.dimen.volume_panel_bottom_bar_horizontal_padding) - if (state.bottomBarComponent.isVisible) { - with(state.bottomBarComponent.component as ComposeVolumePanelUiComponent) { - Content( - Modifier.navigationBarsPadding() + if (components.bottomBarComponent.isVisible) { + val horizontalPadding = + dimensionResource(R.dimen.volume_panel_bottom_bar_horizontal_padding) + Box( + modifier = + Modifier.fillMaxWidth() + .navigationBarsPadding() .padding( start = horizontalPadding, end = horizontalPadding, bottom = dimensionResource(R.dimen.volume_panel_bottom_bar_bottom_padding), - ) - ) + ), + contentAlignment = Alignment.Center, + ) { + with(components.bottomBarComponent.component as ComposeVolumePanelUiComponent) { + Content(Modifier) + } } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 3dfe65a4f736..51c008ab686a 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -36,7 +36,7 @@ import com.android.systemui.plugins.clocks.ClockMetadata import com.android.systemui.plugins.clocks.ClockProvider import com.android.systemui.plugins.clocks.ClockProviderPlugin import com.android.systemui.plugins.clocks.ClockSettings -import com.android.systemui.util.Assert +import com.android.systemui.util.ThreadAssert import java.io.PrintWriter import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicBoolean @@ -89,6 +89,7 @@ open class ClockRegistry( val keepAllLoaded: Boolean, subTag: String, var isTransitClockEnabled: Boolean = false, + val assert: ThreadAssert = ThreadAssert(), ) { private val TAG = "${ClockRegistry::class.simpleName} ($subTag)" private val logger: Logger = @@ -286,7 +287,7 @@ open class ClockRegistry( @OpenForTesting open fun querySettings() { - assertNotMainThread() + assert.isNotMainThread() val result = try { val json = @@ -313,7 +314,7 @@ open class ClockRegistry( @OpenForTesting open fun applySettings(value: ClockSettings?) { - assertNotMainThread() + assert.isNotMainThread() try { value?.metadata?.put(KEY_TIMESTAMP, System.currentTimeMillis()) @@ -339,16 +340,6 @@ open class ClockRegistry( settings = value } - @OpenForTesting - protected open fun assertMainThread() { - Assert.isMainThread() - } - - @OpenForTesting - protected open fun assertNotMainThread() { - Assert.isNotMainThread() - } - private var isClockChanged = AtomicBoolean(false) private fun triggerOnCurrentClockChanged() { val shouldSchedule = isClockChanged.compareAndSet(false, true) @@ -357,7 +348,7 @@ open class ClockRegistry( } scope.launch(mainDispatcher) { - assertMainThread() + assert.isMainThread() isClockChanged.set(false) clockChangeListeners.forEach { it.onCurrentClockChanged() } } @@ -371,7 +362,7 @@ open class ClockRegistry( } scope.launch(mainDispatcher) { - assertMainThread() + assert.isMainThread() isClockListChanged.set(false) clockChangeListeners.forEach { it.onAvailableClocksChanged() } } @@ -585,7 +576,7 @@ open class ClockRegistry( * Calling from main thread to make sure the access is thread safe. */ fun registerClockChangeListener(listener: ClockChangeListener) { - assertMainThread() + assert.isMainThread() clockChangeListeners.add(listener) } @@ -595,7 +586,7 @@ open class ClockRegistry( * Calling from main thread to make sure the access is thread safe. */ fun unregisterClockChangeListener(listener: ClockChangeListener) { - assertMainThread() + assert.isMainThread() clockChangeListeners.remove(listener) } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioModeInteractorTest.kt index 4dbf865475a4..fe34361540e1 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioModeInteractorTest.kt @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.settingslib.volume.domain.interactor +package com.android.systemui.volume.domain.interactor import android.media.AudioManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.settingslib.BaseTest -import com.android.settingslib.volume.data.repository.FakeAudioRepository +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor +import com.android.systemui.SysuiTestCase +import com.android.systemui.volume.data.repository.FakeAudioRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn @@ -34,7 +35,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @SmallTest -class AudioModeInteractorTest : BaseTest() { +class AudioModeInteractorTest : SysuiTestCase() { private val testScope = TestScope() private val fakeAudioRepository = FakeAudioRepository() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt new file mode 100644 index 000000000000..ec37925af0f3 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain + +import android.media.AudioManager +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.volume.audioModeInteractor +import com.android.systemui.volume.audioRepository +import com.android.systemui.volume.localMediaRepository +import com.android.systemui.volume.mediaController +import com.android.systemui.volume.mediaControllerRepository +import com.android.systemui.volume.mediaOutputInteractor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@RunWithLooper(setAsMainLooper = true) +class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: MediaOutputAvailabilityCriteria + + @Before + fun setup() { + with(kosmos) { + whenever(mediaController.packageName).thenReturn("test.pkg") + whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {})) + whenever(mediaController.playbackState).thenReturn(PlaybackState.Builder().build()) + + mediaControllerRepository.setActiveLocalMediaController(mediaController) + + underTest = MediaOutputAvailabilityCriteria(mediaOutputInteractor, audioModeInteractor) + } + } + + @Test + fun notInCallAndHasDevices_isAvailable_true() { + with(kosmos) { + testScope.runTest { + audioRepository.setMode(AudioManager.MODE_NORMAL) + localMediaRepository.updateMediaDevices(listOf(mock {})) + + val isAvailable by collectLastValue(underTest.isAvailable()) + runCurrent() + + assertThat(isAvailable).isTrue() + } + } + } + @Test + fun inCallAndHasDevices_isAvailable_false() { + with(kosmos) { + testScope.runTest { + audioRepository.setMode(AudioManager.MODE_IN_CALL) + localMediaRepository.updateMediaDevices(listOf(mock {})) + + val isAvailable by collectLastValue(underTest.isAvailable()) + runCurrent() + + assertThat(isAvailable).isFalse() + } + } + } + + @Test + fun notInCallAndHasDevices_isAvailable_false() { + with(kosmos) { + testScope.runTest { + audioRepository.setMode(AudioManager.MODE_NORMAL) + localMediaRepository.updateMediaDevices(emptyList()) + + val isAvailable by collectLastValue(underTest.isAvailable()) + runCurrent() + + assertThat(isAvailable).isFalse() + } + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt new file mode 100644 index 000000000000..243aab24b07d --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel + +import android.content.applicationContext +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.volume.localMediaRepository +import com.android.systemui.volume.mediaController +import com.android.systemui.volume.mediaControllerRepository +import com.android.systemui.volume.mediaOutputInteractor +import com.android.systemui.volume.panel.mediaOutputActionsInteractor +import com.android.systemui.volume.panel.volumePanelViewModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class MediaOutputViewModelTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val playbackStateBuilder = PlaybackState.Builder() + + private lateinit var underTest: MediaOutputViewModel + + @Before + fun setup() { + with(kosmos) { + underTest = + MediaOutputViewModel( + applicationContext, + testScope.backgroundScope, + volumePanelViewModel, + mediaOutputActionsInteractor, + mediaOutputInteractor, + ) + + with(context.orCreateTestableResources) { + addOverride(R.string.media_output_label_title, "media_output_label_title") + addOverride( + R.string.media_output_title_without_playing, + "media_output_title_without_playing" + ) + } + + whenever(mediaController.packageName).thenReturn("test.pkg") + whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {})) + whenever(mediaController.playbackState).then { playbackStateBuilder.build() } + + mediaControllerRepository.setActiveLocalMediaController(mediaController) + } + } + + @Test + fun playingSession_connectedDeviceViewMode_hasTheDevice() { + with(kosmos) { + testScope.runTest { + playbackStateBuilder.setState(PlaybackState.STATE_PLAYING, 0, 0f) + localMediaRepository.updateCurrentConnectedDevice( + mock { whenever(name).thenReturn("test_device") } + ) + + val connectedDeviceViewModel by collectLastValue(underTest.connectedDeviceViewModel) + runCurrent() + + assertThat(connectedDeviceViewModel!!.label).isEqualTo("media_output_label_title") + assertThat(connectedDeviceViewModel!!.deviceName).isEqualTo("test_device") + } + } + } + + @Test + fun notPlaying_connectedDeviceViewMode_hasTheDevice() { + with(kosmos) { + testScope.runTest { + playbackStateBuilder.setState(PlaybackState.STATE_STOPPED, 0, 0f) + localMediaRepository.updateCurrentConnectedDevice( + mock { whenever(name).thenReturn("test_device") } + ) + + val connectedDeviceViewModel by collectLastValue(underTest.connectedDeviceViewModel) + runCurrent() + + assertThat(connectedDeviceViewModel!!.label) + .isEqualTo("media_output_title_without_playing") + assertThat(connectedDeviceViewModel!!.deviceName).isEqualTo("test_device") + } + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt index 7c993603b810..71866b3957b6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt @@ -50,7 +50,7 @@ class DefaultComponentsLayoutManagerTest : SysuiTestCase() { val component4 = ComponentState(COMPONENT_4, kosmos.mockVolumePanelUiComponent, false) val layout = underTest.layout( - VolumePanelState(0, false), + VolumePanelState(0, false, false), setOf(bottomBarComponentState, component1, component2, component3, component4) ) @@ -71,7 +71,7 @@ class DefaultComponentsLayoutManagerTest : SysuiTestCase() { val component1State = ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false) val component2State = ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false) underTest.layout( - VolumePanelState(0, false), + VolumePanelState(0, false, false), setOf( component1State, component2State, diff --git a/packages/SystemUI/plugin/proguard_plugins.flags b/packages/SystemUI/plugin/proguard_plugins.flags index abac27f0cbe6..23ba8d066bb2 100644 --- a/packages/SystemUI/plugin/proguard_plugins.flags +++ b/packages/SystemUI/plugin/proguard_plugins.flags @@ -7,3 +7,13 @@ -keep class com.android.systemui.log.core.** { *; } + +# This type is used in the plugin API boundary, so ensure the used public methods are kept. +-keepclassmembers class androidx.constraintlayout.widget.ConstraintSet { + public void connect(int, int, int, int, int); + public void constrainWidth(int, int); + public void constrainHeight(int, int); + public int getHeight(int); + public int getWidth(int); + public void setGoneMargin(int, int, int); +} diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml index 5db9eee6a908..109e63c6167a 100644 --- a/packages/SystemUI/res/layout/media_session_view.xml +++ b/packages/SystemUI/res/layout/media_session_view.xml @@ -67,6 +67,18 @@ android:background="@drawable/qs_media_outline_layout_bg" /> + <com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_expanded" + app:layout_constraintStart_toStartOf="@id/album_art" + app:layout_constraintEnd_toEndOf="@id/album_art" + app:layout_constraintTop_toTopOf="@id/album_art" + app:layout_constraintBottom_toBottomOf="@id/album_art" + android:clipToOutline="true" + android:background="@drawable/qs_media_outline_layout_bg" + /> + <!-- Guideline for output switcher --> <androidx.constraintlayout.widget.Guideline android:id="@+id/center_vertical_guideline" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7537a003275d..a681da3adc4e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -608,7 +608,6 @@ <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen> <dimen name="volume_panel_corner_radius">52dp</dimen> - <dimen name="volume_panel_content_padding">24dp</dimen> <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen> <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e401c71b3716..a7d93e70fda3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1416,6 +1416,9 @@ <!-- Indication on the keyguard that appears when a trust agents unlocks the device. [CHAR LIMIT=40] --> <string name="keyguard_indication_trust_unlocked">Kept unlocked by TrustAgent</string> + <!-- Message asking the user to authenticate with primary authentication methods (PIN/pattern/password) or biometrics after the device is locked by adaptive auth. [CHAR LIMIT=60] --> + <string name="kg_prompt_after_adaptive_auth_lock">Theft protection\nDevice locked, too many unlock attempts</string> + <!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] --> <string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string> @@ -1532,6 +1535,12 @@ <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring (<xliff:g id="volume level" example="56">%1$s</xliff:g>)</string> + <!-- Title with application label for media output settings. [CHAR LIMIT=20] --> + <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string> + + <!-- Title for media output settings without media is playing. [CHAR LIMIT=20] --> + <string name="media_output_title_without_playing">Audio will play on</string> + <!-- Name of special SystemUI debug settings --> <string name="system_ui_tuner">System UI Tuner</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index ab3cacb27191..f1d4d71d1cc4 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -953,13 +953,13 @@ <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> </style> - <style name="Theme.VolumePanelActivity" parent="@style/Theme.SystemUI"> + <style name="Theme.VolumePanelActivity" + parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:windowActionBar">false</item> - <item name="android:windowNoTitle">true</item> - <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. --> - <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item> + <item name="android:backgroundDimEnabled">true</item> + <item name="android:windowCloseOnTouchOutside">true</item> + <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> </style> <style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml index c053b33b4c63..2f2b10f8dc0c 100644 --- a/packages/SystemUI/res/xml/media_session_collapsed.xml +++ b/packages/SystemUI/res/xml/media_session_collapsed.xml @@ -55,6 +55,15 @@ app:layout_constraintBottom_toBottomOf="@+id/album_art" /> <Constraint + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_collapsed" + app:layout_constraintStart_toStartOf="@+id/album_art" + app:layout_constraintEnd_toEndOf="@+id/album_art" + app:layout_constraintTop_toTopOf="@+id/album_art" + app:layout_constraintBottom_toBottomOf="@+id/album_art" /> + + <Constraint android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml index 8bf7560d6ddb..0140d52bd175 100644 --- a/packages/SystemUI/res/xml/media_session_expanded.xml +++ b/packages/SystemUI/res/xml/media_session_expanded.xml @@ -48,6 +48,15 @@ app:layout_constraintBottom_toBottomOf="@+id/album_art" /> <Constraint + android:id="@+id/loading_effect_view" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_expanded" + app:layout_constraintStart_toStartOf="@+id/album_art" + app:layout_constraintEnd_toEndOf="@+id/album_art" + app:layout_constraintTop_toTopOf="@+id/album_art" + app:layout_constraintBottom_toBottomOf="@+id/album_art" /> + + <Constraint android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 91e6b6248d34..8b2a0ec27011 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -43,7 +43,6 @@ import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags.REGION_SAMPLING import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.log.core.Logger @@ -316,7 +315,7 @@ constructor( object : KeyguardUpdateMonitorCallback() { override fun onKeyguardVisibilityChanged(visible: Boolean) { isKeyguardVisible = visible - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { if (!isKeyguardVisible) { clock?.run { smallClock.animations.doze(if (isDozing) 1f else 0f) @@ -410,7 +409,7 @@ constructor( parent.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { listenForDozing(this) - if (KeyguardShadeMigrationNssl.isEnabled) { + if (migrateClocksToBlueprint()) { listenForDozeAmountTransition(this) listenForAnyStateToAodTransition(this) } else { @@ -421,9 +420,11 @@ constructor( smallTimeListener?.update(shouldTimeListenerRun) largeTimeListener?.update(shouldTimeListenerRun) - // Query ZenMode data - zenModeCallback.onZenChanged(zenModeController.zen) - zenModeCallback.onNextAlarmChanged() + bgExecutor.execute { + // Query ZenMode data + zenModeCallback.onZenChanged(zenModeController.zen) + zenModeCallback.onNextAlarmChanged() + } } fun unregisterListeners() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 2db3795cbad7..e621ffe4cbc4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -47,7 +47,6 @@ import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.LogLevel; @@ -349,7 +348,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } int getNotificationIconAreaHeight() { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { return 0; } else if (NotificationIconContainerRefactor.isEnabled()) { return mAodIconContainer != null ? mAodIconContainer.getHeight() : 0; @@ -597,7 +596,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } private void updateAodIcons() { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { NotificationIconContainer nic = (NotificationIconContainer) mView.findViewById( com.android.systemui.res.R.id.left_aligned_notification_icon_container); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 3585feb3442d..84c8ea708031 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -19,6 +19,7 @@ package com.android.keyguard; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.WindowInsets.Type.ime; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT; @@ -126,6 +127,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_TRUSTAGENT_EXPIRED: return R.string.kg_prompt_reason_timeout_password; + case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST: + return R.string.kg_prompt_after_adaptive_auth_lock; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index db7ff888356c..bf8900da887a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -331,6 +331,9 @@ public class KeyguardPatternViewController case PROMPT_REASON_TRUSTAGENT_EXPIRED: resId = R.string.kg_prompt_reason_timeout_pattern; break; + case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST: + resId = R.string.kg_prompt_after_adaptive_auth_lock; + break; case PROMPT_REASON_NONE: break; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index fcff0dbc0878..bcab6f054dd6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT; @@ -138,6 +139,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_TRUSTAGENT_EXPIRED: return R.string.kg_prompt_reason_timeout_pin; + case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST: + return R.string.kg_prompt_after_adaptive_auth_lock; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 83b1a2cbbf52..3e87c1b60581 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -67,6 +67,11 @@ public interface KeyguardSecurityView { int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8; /** + * Some auth is required because adaptive auth has determined risk + */ + int PROMPT_REASON_ADAPTIVE_AUTH_REQUEST = 9; + + /** * Strong auth is required because the device has just booted because of an automatic * mainline update. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 1758831203d6..9421f150a38a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -54,7 +54,6 @@ import com.android.systemui.animation.ViewHierarchyAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.plugins.clocks.ClockController; @@ -231,7 +230,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } mDumpManager.registerDumpable(getInstanceName(), this); - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { startCoroutines(EmptyCoroutineContext.INSTANCE); } } @@ -511,7 +510,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(layout); int guideline; - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { guideline = R.id.split_shade_guideline; } else { guideline = R.id.qs_edge_guideline; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 536f3afdd575..38c2829e27f6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -35,6 +35,7 @@ import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; 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_LOCKOUT; @@ -382,6 +383,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private List<SubscriptionInfo> mSubscriptionInfo; @VisibleForTesting protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + private boolean mFingerprintDetectRunning; private boolean mIsDreaming; private boolean mLogoutEnabled; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -1003,6 +1005,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean wasCancellingRestarting = mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING; mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + mFingerprintDetectRunning = false; if (wasCancellingRestarting) { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else { @@ -1111,6 +1114,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING; mFingerprintRunningState = fingerprintRunningState; + if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) { + mFingerprintDetectRunning = false; + } mLogger.logFingerprintRunningState(mFingerprintRunningState); // Clients of KeyguardUpdateMonitor don't care about the internal state about the // asynchronousness of the cancel cycle. So only notify them if the actually running state @@ -1570,6 +1576,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return isEncrypted || isLockDown; } + /** + * Whether the device is locked by adaptive auth + */ + public boolean isDeviceLockedByAdaptiveAuth(int userId) { + return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId), + SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST); + } + private boolean containsFlag(int haystack, int needle) { return (haystack & needle) != 0; } @@ -1835,8 +1849,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { - handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric); + // Fingerprint lifecycle ends + if (mHandler.hasCallbacks(mFpCancelNotReceived)) { + mLogger.d("onFingerprintDetected()" + + " triggered while waiting for cancellation, removing watchdog"); + mHandler.removeCallbacks(mFpCancelNotReceived); + } + // Don't send cancel if detect succeeds + mFingerprintCancelSignal = null; setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); + handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric); } }; @@ -2099,6 +2121,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting void resetBiometricListeningState() { mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + mFingerprintDetectRunning = false; } @VisibleForTesting @@ -2537,8 +2560,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported()); - final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING + final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; + final boolean runningOrRestarting = running || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING; + final boolean runDetect = !isUnlockingWithFingerprintAllowed(); + if (runningOrRestarting && !shouldListenForFingerprint) { if (action == BIOMETRIC_ACTION_START) { mLogger.v("Ignoring stopListeningForFingerprint()"); @@ -2550,7 +2576,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.v("Ignoring startListeningForFingerprint()"); return; } - startListeningForFingerprint(); + startListeningForFingerprint(runDetect); + } else if (running && (runDetect != mFingerprintDetectRunning)) { + if (action == BIOMETRIC_ACTION_STOP) { + if (runDetect) { + mLogger.v("Allowing startListeningForFingerprint(detect) despite" + + " BIOMETRIC_ACTION_STOP since auth was running before."); + } else { + mLogger.v("Ignoring startListeningForFingerprint() switch detect -> auth"); + return; + } + } + startListeningForFingerprint(runDetect); } } @@ -2809,7 +2846,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && biometricEnabledForUser && !isUserInLockdown(user); final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed(); - final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled(); final boolean shouldListenBouncerState = !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing; @@ -2872,7 +2908,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void startListeningForFingerprint() { + private void startListeningForFingerprint(boolean runDetect) { final int userId = mSelectedUserInteractor.getSelectedUserId(); final boolean unlockPossible = isUnlockWithFingerprintPossible(userId); if (mFingerprintCancelSignal != null) { @@ -2902,18 +2938,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintInteractiveToAuthProvider.getVendorExtension(userId)); } - if (!isUnlockingWithFingerprintAllowed()) { + if (runDetect) { mLogger.v("startListeningForFingerprint - detect"); mFpm.detectFingerprint( mFingerprintCancelSignal, mFingerprintDetectionCallback, fingerprintAuthenticateOptions); + mFingerprintDetectRunning = true; } else { mLogger.v("startListeningForFingerprint"); mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal, mFingerprintAuthenticationCallback, null /* handler */, fingerprintAuthenticateOptions); + mFingerprintDetectRunning = false; } setFingerprintRunningState(BIOMETRIC_STATE_RUNNING); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 9ebae9023d44..2000028dff41 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -23,7 +24,6 @@ import android.util.Property; import android.view.View; import com.android.app.animation.Interpolators; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.LogLevel; import com.android.systemui.statusbar.StatusBarState; @@ -109,7 +109,7 @@ public class KeyguardVisibilityHelper { animProps.setDelay(0).setDuration(160); log("goingToFullShade && !keyguardFadingAway"); } - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { log("Using LockscreenToGoneTransition 1"); } else { PropertyAnimator.setProperty( @@ -167,7 +167,7 @@ public class KeyguardVisibilityHelper { animProps, true /* animate */); } else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { log("Using GoneToAodTransition"); mKeyguardViewVisibilityAnimating = false; } else { @@ -183,7 +183,7 @@ public class KeyguardVisibilityHelper { mView.setVisibility(View.VISIBLE); } } else { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { log("Using LockscreenToGoneTransition 2"); } else { log("Direct set Visibility to GONE"); diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index 878a5d88f164..a0f15efe7025 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -33,6 +33,7 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers; import com.android.systemui.res.R; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.clocks.DefaultClockProvider; +import com.android.systemui.util.ThreadAssert; import dagger.Module; import dagger.Provides; @@ -74,7 +75,8 @@ public abstract class ClockRegistryModule { clockBuffers, /* keepAllLoaded = */ false, /* subTag = */ "System", - /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK)); + /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK), + new ThreadAssert()); registry.registerListeners(); return registry; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 3397906aa6ea..0bd44f0f3901 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -17,6 +17,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.Flags.customBiometricPrompt; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; @@ -401,37 +402,8 @@ public class AuthContainerView extends LinearLayout @Nullable FaceSensorPropertiesInternal faceProps, @NonNull VibratorHelper vibratorHelper ) { - if (Utils.isBiometricAllowed(config.mPromptInfo)) { - mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication( - config.mPromptInfo, - config.mUserId, - config.mOperationId, - new BiometricModalities(fpProps, faceProps), - config.mOpPackageName); - - if (constraintBp()) { - mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null, - // TODO(b/201510778): This uses the wrong timeout in some cases - getJankListener(mLayout, TRANSIT, - BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), - mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, - vibratorHelper); - } else { - final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate( - R.layout.biometric_prompt_layout, null, false); - mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController, - // TODO(b/201510778): This uses the wrong timeout in some cases - getJankListener(view, TRANSIT, - BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), - mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, - vibratorHelper); - - // TODO(b/251476085): migrate these dependencies - if (fpProps != null && fpProps.isAnyUdfpsType()) { - view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps), - config.mScaleProvider); - } - } + if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) { + addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper); } else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) { addCredentialView(true, false); } else { @@ -439,6 +411,44 @@ public class AuthContainerView extends LinearLayout } } + + private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater, + @NonNull PromptViewModel viewModel, + @Nullable FingerprintSensorPropertiesInternal fpProps, + @Nullable FaceSensorPropertiesInternal faceProps, + @NonNull VibratorHelper vibratorHelper) { + mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication( + config.mPromptInfo, + config.mUserId, + config.mOperationId, + new BiometricModalities(fpProps, faceProps), + config.mOpPackageName); + + if (constraintBp()) { + mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null, + // TODO(b/201510778): This uses the wrong timeout in some cases + getJankListener(mLayout, TRANSIT, + BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), + mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, + vibratorHelper); + } else { + final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate( + R.layout.biometric_prompt_layout, null, false); + mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController, + // TODO(b/201510778): This uses the wrong timeout in some cases + getJankListener(view, TRANSIT, + BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), + mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, + vibratorHelper); + + // TODO(b/251476085): migrate these dependencies + if (fpProps != null && fpProps.isAnyUdfpsType()) { + view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps), + config.mScaleProvider); + } + } + } + private void onBackInvoked() { sendEarlyUserCanceled(); animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); @@ -524,7 +534,7 @@ public class AuthContainerView extends LinearLayout () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED)); if (constraintBp()) { // Do nothing on attachment with constraintLayout - } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { + } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) { mBiometricScrollView.addView(mBiometricView.asView()); } else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) { addCredentialView(true /* animatePanel */, false /* animateContents */); 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 31aadf51c4f2..b0cc3bd807dd 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 @@ -22,6 +22,7 @@ import android.content.Context import android.hardware.biometrics.BiometricAuthenticator import android.hardware.biometrics.BiometricConstants import android.hardware.biometrics.BiometricPrompt +import android.hardware.biometrics.Flags.customBiometricPrompt import android.hardware.face.FaceManager import android.text.method.ScrollingMovementMethod import android.util.Log @@ -123,13 +124,6 @@ object BiometricViewBinder { (view as BiometricPromptLayout).updatedFingerprintAffordanceSize } - PromptIconViewBinder.bind( - iconView, - iconOverlayView, - iconSizeOverride, - viewModel, - ) - val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator) // Negative-side (left) buttons @@ -156,6 +150,18 @@ object BiometricViewBinder { view.repeatWhenAttached { // these do not change and need to be set before any size transitions val modalities = viewModel.modalities.first() + + // If there is no biometrics available, biometric prompt is showing just for displaying + // content, no authentication needed. + if (!(customBiometricPrompt() && modalities.isEmpty)) { + PromptIconViewBinder.bind( + iconView, + iconOverlayView, + iconSizeOverride, + viewModel, + ) + } + if (modalities.hasFingerprint) { /** * Load the given [rawResources] immediately so they are cached for use in the diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 2417fe9cd333..a37d9168dfd3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -21,6 +21,7 @@ import android.animation.AnimatorSet import android.animation.ValueAnimator import android.graphics.Outline import android.graphics.Rect +import android.hardware.biometrics.Flags import android.transition.AutoTransition import android.transition.TransitionManager import android.view.Surface @@ -59,6 +60,7 @@ import com.android.systemui.res.R import kotlin.math.abs import kotlin.math.min import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch /** Helper for [BiometricViewBinder] to handle resize transitions. */ @@ -219,6 +221,18 @@ object BiometricViewSizeBinder { view.repeatWhenAttached { var currentSize: PromptSize? = null + val modalities = viewModel.modalities.first() + // TODO(b/288175072): Move all visibility settings together. + // If there is no biometrics available, biometric prompt is showing just for + // displaying content, no authentication needed. + if (Flags.customBiometricPrompt() && modalities.isEmpty) { + smallConstraintSet.setVisibility(iconHolderView.id, View.GONE) + smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE) + smallConstraintSet.setVisibility(R.id.indicator, View.GONE) + mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE) + mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE) + mediumConstraintSet.setVisibility(R.id.indicator, View.GONE) + } lifecycleScope.launch { combine(viewModel.position, viewModel.size, ::Pair).collect { (position, size) -> @@ -299,6 +313,7 @@ object BiometricViewSizeBinder { // TODO(b/251476085): migrate the legacy panel controller and simplify this view.repeatWhenAttached { var currentSize: PromptSize? = null + val modalities = viewModel.modalities.first() lifecycleScope.launch { /** * View is only set visible in BiometricViewSizeBinder once PromptSize is @@ -318,6 +333,9 @@ object BiometricViewSizeBinder { for (v in viewsToHideWhenSmall) { v.showContentOrHide(forceHide = size.isSmall) } + if (Flags.customBiometricPrompt() && modalities.isEmpty) { + iconHolderView.visibility = View.GONE + } if (currentSize == null && size.isSmall) { iconHolderView.alpha = 0f } @@ -328,8 +346,7 @@ object BiometricViewSizeBinder { // TODO(b/302735104): Fix wrong height due to the delay of // PromptContentView. addOnLayoutChangeListener() will cause crash when // showing credential view, since |PromptIconViewModel| won't release - // the - // flow. + // the flow. // propagate size changes to legacy panel controller and animate // transitions view.doOnLayout { 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 index 7b4be0220ff2..9c28f1c16546 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt @@ -115,17 +115,11 @@ constructor( } 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() - lottie?.removeAllLottieOnCompositionLoadedListener() - windowManager.get().removeView(it) - } + if (overlayView?.isAttachedToWindow == true) { + return } overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt index 8197145f9646..c25e748f8668 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt @@ -50,6 +50,7 @@ import com.android.systemui.res.R.string.kg_fp_not_recognized import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin +import com.android.systemui.res.R.string.kg_prompt_after_adaptive_auth_lock import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock import com.android.systemui.res.R.string.kg_prompt_after_update_password import com.android.systemui.res.R.string.kg_prompt_after_update_pattern @@ -208,6 +209,11 @@ constructor( } else { faceLockedOut(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value) } + } else if (flags.isSomeAuthRequiredAfterAdaptiveAuthRequest) { + authRequiredAfterAdaptiveAuthRequest( + currentSecurityMode, + isFingerprintAuthCurrentlyAllowed.value + ) } else if ( trustOrBiometricsAvailable && flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout @@ -464,6 +470,34 @@ private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerM }.toMessage() } +private fun authRequiredAfterAdaptiveAuthRequest( + securityMode: SecurityMode, + fpAuthIsAllowed: Boolean +): BouncerMessageModel { + return if (fpAuthIsAllowed) authRequiredAfterAdaptiveAuthRequestFingerprintAllowed(securityMode) + else + return when (securityMode) { + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_adaptive_auth_lock) + SecurityMode.Password -> + Pair(keyguard_enter_password, kg_prompt_after_adaptive_auth_lock) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_adaptive_auth_lock) + else -> Pair(0, 0) + }.toMessage() +} + +private fun authRequiredAfterAdaptiveAuthRequestFingerprintAllowed( + securityMode: SecurityMode +): BouncerMessageModel { + return when (securityMode) { + SecurityMode.Pattern -> + Pair(kg_unlock_with_pattern_or_fp, kg_prompt_after_adaptive_auth_lock) + SecurityMode.Password -> + Pair(kg_unlock_with_password_or_fp, kg_prompt_after_adaptive_auth_lock) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_after_adaptive_auth_lock) + else -> Pair(0, 0) + }.toMessage() +} + private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessageModel { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern) diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index 8178adef49b2..a0aaa906802a 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -79,7 +79,7 @@ interface BaseComposeFacade { fun setVolumePanelActivityContent( activity: ComponentActivity, viewModel: VolumePanelViewModel, - onDismissAnimationFinished: () -> Unit, + onDismiss: () -> Unit, ) /** Create a [View] to represent [viewModel] on screen. */ diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 41ce3fd11e8a..7a24d7693035 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -29,7 +29,6 @@ import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW import com.android.systemui.keyguard.shared.ComposeLockscreen -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor @@ -52,15 +51,11 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token - // Internal keyguard dependencies - KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor - // SceneContainer dependencies SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta } SceneContainerFlag.getMainStaticFlag() dependsOn MIGRATE_KEYGUARD_STATUS_BAR_VIEW // ComposeLockscreen dependencies - ComposeLockscreen.token dependsOn KeyguardShadeMigrationNssl.token ComposeLockscreen.token dependsOn keyguardBottomAreaRefactor ComposeLockscreen.token dependsOn migrateClocksToBlueprint } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt index 14fda5e96a9c..efbd59f6ce17 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt @@ -48,10 +48,14 @@ abstract class FlagDependenciesBase( private var unmetDependencies = emptyList<Dependency>() override fun start() { + if (!handler.enableDependencies) { + return + } defineDependencies() allDependencies = workingDependencies.toList() unmetDependencies = workingDependencies.filter { !it.isMet } workingDependencies.clear() + handler.onCollected(allDependencies) if (unmetDependencies.isNotEmpty()) { handler.warnAboutBadFlagConfiguration(all = allDependencies, unmet = unmetDependencies) } @@ -106,14 +110,24 @@ abstract class FlagDependenciesBase( /** Add a dependency to the working list */ private fun addDependency(first: FlagToken, second: FlagToken) { - if (!Compile.IS_DEBUG) return // `user` builds should omit all this code + if (!handler.enableDependencies) return workingDependencies.add( Dependency(first.name, first.isEnabled, second.name, second.isEnabled) ) } - /** An interface which handles a warning about a bad flag configuration. */ + /** An interface which handles dependency collection. */ interface Handler { + /** + * Should FlagDependencies do anything? + * + * @return false for user builds so that we skip this overhead. + */ + val enableDependencies: Boolean + get() = Compile.IS_DEBUG + /** Handle the complete list of dependencies. */ + fun onCollected(all: List<Dependency>) {} + /** Handle a bad flag configuration. */ fun warnAboutBadFlagConfiguration(all: List<Dependency>, unmet: List<Dependency>) } } @@ -133,7 +147,7 @@ constructor( all: List<FlagDependenciesBase.Dependency>, unmet: List<FlagDependenciesBase.Dependency> ) { - val title = "Invalid flag dependencies: ${unmet.size} of ${all.size}" + val title = "Invalid flag dependencies: ${unmet.size}" val details = unmet.joinToString("\n") { it.shortUnmetString() } Log.e("FlagDependencies", "$title:\n$details") val channel = NotificationChannel("FLAGS", "Flags", NotificationManager.IMPORTANCE_DEFAULT) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index e23ec894f8f3..00ec1a14bb93 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -404,6 +404,7 @@ public class KeyguardIndicationRotateTextViewController extends public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11; public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12; public static final int INDICATION_IS_DISMISSIBLE = 13; + public static final int INDICATION_TYPE_ADAPTIVE_AUTH = 14; @IntDef({ INDICATION_TYPE_NONE, @@ -419,7 +420,8 @@ public class KeyguardIndicationRotateTextViewController extends INDICATION_TYPE_REVERSE_CHARGING, INDICATION_TYPE_BIOMETRIC_MESSAGE, INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, - INDICATION_IS_DISMISSIBLE + INDICATION_IS_DISMISSIBLE, + INDICATION_TYPE_ADAPTIVE_AUTH }) @Retention(RetentionPolicy.SOURCE) public @interface IndicationType{} @@ -455,6 +457,8 @@ public class KeyguardIndicationRotateTextViewController extends return "biometric_message"; case INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP: return "biometric_message_followup"; + case INDICATION_TYPE_ADAPTIVE_AUTH: + return "adaptive_auth"; default: return "unknown[" + type + "]"; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 86b99ecac66c..e35c5a636bde 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -53,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel import com.android.systemui.plugins.FalsingManager @@ -98,6 +99,7 @@ constructor( private val falsingManager: FalsingManager, private val aodAlphaViewModel: AodAlphaViewModel, private val keyguardClockViewModel: KeyguardClockViewModel, + private val smartspaceViewModel: KeyguardSmartspaceViewModel, private val lockscreenContentViewModel: LockscreenContentViewModel, private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>, private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder, @@ -148,6 +150,7 @@ constructor( keyguardRootView, keyguardBlueprintViewModel, keyguardClockViewModel, + smartspaceViewModel, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index b5ca79e71c33..641b9677c2e5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -30,6 +30,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -690,18 +691,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } else { resetStateLocked(); } - } else { - if (lastSimStateWasLocked && mShowing) { - if (DEBUG_SIM_STATES) { - Log.d(TAG, "SIM moved to " - + "NOT_READY/ABSENT/UNKNOWN when the previous state " - + "was locked. Reset the state."); - } + } + if (simState == TelephonyManager.SIM_STATE_ABSENT) { + // MVNO SIMs can become transiently NOT_READY when switching networks, + // so we should only lock when they are ABSENT. + if (lastSimStateWasLocked) { + if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the " + + "previous state was locked. Reset the state."); resetStateLocked(); } + mSimWasLocked.append(slotId, false); } - - mSimWasLocked.append(slotId, false); } break; case TelephonyManager.SIM_STATE_PIN_REQUIRED: @@ -921,15 +921,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; } else if ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) { return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; + } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 + || mUpdateMonitor.isFingerprintLockedOut())) { + return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; + } else if ((strongAuth & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST) != 0) { + return KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST; } else if (trustAgentsEnabled && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; } else if (trustAgentsEnabled && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) { return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; - } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 - || mUpdateMonitor.isFingerprintLockedOut())) { - return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) { return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; } else if (any && (strongAuth 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 405d1d46456c..78749ead7ef9 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 @@ -241,6 +241,9 @@ constructor( /** * When the lockscreen can be dismissed, emit an alpha value as the user swipes up. This is * useful just before the code commits to moving to GONE. + * + * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input + * signal should be sent directly to transitions. */ val dismissAlpha: Flow<Float?> = combine( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt deleted file mode 100644 index 23642a741fb8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.keyguard.shared - -import com.android.systemui.Flags -import com.android.systemui.flags.FlagToken -import com.android.systemui.flags.RefactorFlagUtils - -/** Helper for reading or using the keyguard shade migration nssl flag state. */ -@Suppress("NOTHING_TO_INLINE") -object KeyguardShadeMigrationNssl { - /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL - - /** A token used for dependency declaration */ - val token: FlagToken - get() = FlagToken(FLAG_NAME, isEnabled) - - /** Is the refactor enabled */ - @JvmStatic - inline val isEnabled - get() = Flags.keyguardShadeMigrationNssl() - - /** - * 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/keyguard/shared/model/AuthenticationFlags.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt index cf5b88fde3dc..08904b6ffa86 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt @@ -60,6 +60,12 @@ data class AuthenticationFlags(val userId: Int, val flag: Int) { LockPatternUtils.StrongAuthTracker .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT ) + + val isSomeAuthRequiredAfterAdaptiveAuthRequest = + containsFlag( + flag, + LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST + ) } private fun containsFlag(haystack: Int, needle: Int): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 6e70368476ed..66fc99567d42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.Intra import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached import javax.inject.Inject import kotlin.math.max @@ -84,6 +85,7 @@ constructor( constraintLayout: ConstraintLayout, viewModel: KeyguardBlueprintViewModel, clockViewModel: KeyguardClockViewModel, + smartspaceViewModel: KeyguardSmartspaceViewModel, ) { constraintLayout.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { @@ -108,10 +110,18 @@ constructor( ) { BaseBlueprintTransition(clockViewModel) .addTransition( - IntraBlueprintTransition(Config.DEFAULT, clockViewModel) + IntraBlueprintTransition( + Config.DEFAULT, + clockViewModel, + smartspaceViewModel + ) ) } else { - IntraBlueprintTransition(Config.DEFAULT, clockViewModel) + IntraBlueprintTransition( + Config.DEFAULT, + clockViewModel, + smartspaceViewModel + ) } runTransition(constraintLayout, transition, Config.DEFAULT) { @@ -136,7 +146,11 @@ constructor( runTransition( constraintLayout, - IntraBlueprintTransition(transition, clockViewModel), + IntraBlueprintTransition( + transition, + clockViewModel, + smartspaceViewModel + ), transition, ) { cs.applyTo(constraintLayout) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 5604ef23a142..c58a03c05a09 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -43,7 +43,6 @@ import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.TintedIcon import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel @@ -146,7 +145,7 @@ object KeyguardRootViewBinder { } } - if (KeyguardShadeMigrationNssl.isEnabled) { + if (migrateClocksToBlueprint()) { launch { viewModel.burnInLayerVisibility.collect { visibility -> childViews[burnInLayerId]?.visibility = visibility @@ -316,7 +315,7 @@ object KeyguardRootViewBinder { } } - if (KeyguardShadeMigrationNssl.isEnabled) { + if (migrateClocksToBlueprint()) { burnInParams.update { current -> current.copy(translationY = { childViews[burnInLayerId]?.translationY }) } @@ -415,7 +414,9 @@ object KeyguardRootViewBinder { configuration: ConfigurationState, screenOffAnimationController: ScreenOffAnimationController, ) { - KeyguardShadeMigrationNssl.assertInLegacyMode() + if (migrateClocksToBlueprint()) { + throw IllegalStateException("should only be called in legacy code paths") + } if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return coroutineScope { val iconAppearTranslationPx = @@ -444,7 +445,7 @@ object KeyguardRootViewBinder { } when { !isVisible.isAnimating -> { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { translationY = 0f } visibility = @@ -494,7 +495,7 @@ object KeyguardRootViewBinder { animatorListener: Animator.AnimatorListener, ) { if (animate) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { translationY = -iconAppearTranslation.toFloat() } alpha = 0f @@ -502,19 +503,19 @@ object KeyguardRootViewBinder { .alpha(1f) .setInterpolator(Interpolators.LINEAR) .setDuration(AOD_ICONS_APPEAR_DURATION) - .apply { if (KeyguardShadeMigrationNssl.isEnabled) animateInIconTranslation() } + .apply { if (migrateClocksToBlueprint()) animateInIconTranslation() } .setListener(animatorListener) .start() } else { alpha = 1.0f - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { translationY = 0f } } } private fun View.animateInIconTranslation() { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index bc9671e65f24..77f7ac8571dd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule.Companion.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION +import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import java.util.Optional import javax.inject.Inject @@ -65,6 +66,7 @@ constructor( communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, clockSection: ClockSection, smartspaceSection: SmartspaceSection, + keyguardSliceViewSection: KeyguardSliceViewSection, udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection, ) : KeyguardBlueprint { override val id: String = DEFAULT @@ -83,6 +85,7 @@ constructor( aodBurnInSection, communalTutorialIndicatorSection, clockSection, + keyguardSliceViewSection, defaultDeviceEntrySection, udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index d118d4d11948..55b2381c79e4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule +import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.util.kotlin.getOrNull import java.util.Optional @@ -60,6 +61,7 @@ constructor( communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, clockSection: ClockSection, smartspaceSection: SmartspaceSection, + keyguardSliceViewSection: KeyguardSliceViewSection, udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection, ) : KeyguardBlueprint { override val id: String = SHORTCUTS_BESIDE_UDFPS @@ -78,6 +80,7 @@ constructor( aodBurnInSection, communalTutorialIndicatorSection, clockSection, + keyguardSliceViewSection, defaultDeviceEntrySection, udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt index a7075d97459e..3adeb2aeb283 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt @@ -20,10 +20,12 @@ import android.transition.TransitionSet import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel class IntraBlueprintTransition( config: IntraBlueprintTransition.Config, clockViewModel: KeyguardClockViewModel, + smartspaceViewModel: KeyguardSmartspaceViewModel, ) : TransitionSet() { enum class Type( @@ -56,7 +58,7 @@ class IntraBlueprintTransition( Type.NoTransition -> {} Type.DefaultClockStepping -> addTransition(clockViewModel.clock?.let { DefaultClockSteppingTransition(it) }) - else -> addTransition(ClockSizeTransition(config, clockViewModel)) + else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt index 9a1fcc1a6a51..282c4952d557 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt @@ -22,7 +22,6 @@ import android.view.View import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.Flags.migrateClocksToBlueprint -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.res.R @@ -37,7 +36,7 @@ constructor( ) : KeyguardSection() { private lateinit var burnInLayer: AodBurnInLayer override fun addViews(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } @@ -58,16 +57,14 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } - if (migrateClocksToBlueprint()) { - clockViewModel.burnInLayer = burnInLayer - } + clockViewModel.burnInLayer = burnInLayer } override fun applyConstraints(constraintSet: ConstraintSet) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt index ad589dfcff9e..3d9c04e39679 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt @@ -28,7 +28,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.common.ui.ConfigurationState -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.res.R import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore @@ -59,7 +58,7 @@ constructor( private lateinit var nic: NotificationIconContainer override fun addViews(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } nic = @@ -78,7 +77,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } @@ -99,7 +98,7 @@ constructor( } override fun applyConstraints(constraintSet: ConstraintSet) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } val bottomMargin = @@ -114,12 +113,9 @@ constructor( BOTTOM } constraintSet.apply { - if (migrateClocksToBlueprint()) { - connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin) - setGoneMargin(nicId, BOTTOM, bottomMargin) - } else { - connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin) - } + connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin) + setGoneMargin(nicId, BOTTOM, bottomMargin) + connect( nicId, START, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt index 75132a59eb88..218af2994f4a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt @@ -27,7 +27,6 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.shade.LargeScreenHeaderHelper @@ -71,7 +70,7 @@ constructor( mainDispatcher, ) { override fun applyConstraints(constraintSet: ConstraintSet) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } constraintSet.apply { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt index 851a45f31705..390b39f1e202 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -31,8 +31,8 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.keyguard.KeyguardStatusView import com.android.keyguard.dagger.KeyguardStatusViewComponent +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.KeyguardViewConfigurator -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.media.controls.ui.KeyguardMediaController import com.android.systemui.res.R @@ -58,7 +58,7 @@ constructor( private val statusViewId = R.id.keyguard_status_view override fun addViews(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available. @@ -83,7 +83,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (KeyguardShadeMigrationNssl.isEnabled) { + if (migrateClocksToBlueprint()) { constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let { val statusViewComponent = keyguardStatusViewComponentFactory.build(it, context.display) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt new file mode 100644 index 000000000000..d572c51d1146 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.Barrier +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.Flags.migrateClocksToBlueprint +import com.android.systemui.keyguard.shared.model.KeyguardSection +import com.android.systemui.res.R +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController +import javax.inject.Inject + +class KeyguardSliceViewSection +@Inject +constructor( + val smartspaceController: LockscreenSmartspaceController, +) : KeyguardSection() { + override fun addViews(constraintLayout: ConstraintLayout) { + if (!migrateClocksToBlueprint()) return + if (smartspaceController.isEnabled()) return + + constraintLayout.findViewById<View?>(R.id.keyguard_slice_view)?.let { + (it.parent as ViewGroup).removeView(it) + constraintLayout.addView(it) + } + } + + override fun bindData(constraintLayout: ConstraintLayout) {} + + override fun applyConstraints(constraintSet: ConstraintSet) { + if (!migrateClocksToBlueprint()) return + if (smartspaceController.isEnabled()) return + + constraintSet.apply { + connect( + R.id.keyguard_slice_view, + ConstraintSet.START, + ConstraintSet.PARENT_ID, + ConstraintSet.START + ) + connect( + R.id.keyguard_slice_view, + ConstraintSet.END, + ConstraintSet.PARENT_ID, + ConstraintSet.END + ) + constrainHeight(R.id.keyguard_slice_view, ConstraintSet.WRAP_CONTENT) + + connect( + R.id.keyguard_slice_view, + ConstraintSet.TOP, + R.id.lockscreen_clock_view, + ConstraintSet.BOTTOM + ) + + createBarrier( + R.id.smart_space_barrier_bottom, + Barrier.BOTTOM, + 0, + *intArrayOf(R.id.keyguard_slice_view) + ) + } + } + + override fun removeViews(constraintLayout: ConstraintLayout) { + if (!migrateClocksToBlueprint()) return + if (smartspaceController.isEnabled()) return + + constraintLayout.removeView(R.id.keyguard_slice_view) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt index 52d94a087110..d0f57c7f9ded 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt @@ -25,8 +25,8 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags @@ -83,7 +83,7 @@ constructor( } override fun addViews(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } // This moves the existing NSSL view to a different parent, as the controller is a @@ -99,7 +99,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt index 8255bcc87400..b0f7a258a4e6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt @@ -57,6 +57,7 @@ constructor( override fun addViews(constraintLayout: ConstraintLayout) { if (!migrateClocksToBlueprint()) return + if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return smartspaceView = smartspaceController.buildAndConnectView(constraintLayout) weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout) dateView = smartspaceController.buildAndConnectDateView(constraintLayout) @@ -83,6 +84,7 @@ constructor( override fun bindData(constraintLayout: ConstraintLayout) { if (!migrateClocksToBlueprint()) return + if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return KeyguardSmartspaceViewBinder.bind( constraintLayout, keyguardClockViewModel, @@ -93,6 +95,7 @@ constructor( override fun applyConstraints(constraintSet: ConstraintSet) { if (!migrateClocksToBlueprint()) return + if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return val horizontalPaddingStart = context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) + context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal) @@ -189,6 +192,7 @@ constructor( override fun removeViews(constraintLayout: ConstraintLayout) { if (!migrateClocksToBlueprint()) return + if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return listOf(smartspaceView, dateView, weatherView).forEach { it?.let { if (it.parent == constraintLayout) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index 3e35ae4b2dc3..2545302ccaa1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -23,8 +23,8 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags @@ -67,7 +67,7 @@ constructor( mainDispatcher, ) { override fun applyConstraints(constraintSet: ConstraintSet) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { return } constraintSet.apply { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index 64cbb3229a57..ab0d4892ae0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -31,6 +31,7 @@ import com.android.app.animation.Interpolators import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import kotlin.math.abs @@ -41,12 +42,13 @@ internal fun View.setRect(rect: Rect) = class ClockSizeTransition( config: IntraBlueprintTransition.Config, clockViewModel: KeyguardClockViewModel, + smartspaceViewModel: KeyguardSmartspaceViewModel, ) : TransitionSet() { init { ordering = ORDERING_TOGETHER if (config.type != Type.SmartspaceVisibility) { - addTransition(ClockFaceOutTransition(config, clockViewModel)) - addTransition(ClockFaceInTransition(config, clockViewModel)) + addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel)) + addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel)) } addTransition(SmartspaceMoveTransition(config, clockViewModel)) } @@ -197,12 +199,13 @@ class ClockSizeTransition( class ClockFaceInTransition( config: IntraBlueprintTransition.Config, val viewModel: KeyguardClockViewModel, + val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { init { duration = CLOCK_IN_MILLIS startDelay = CLOCK_IN_START_DELAY_MILLIS interpolator = CLOCK_IN_INTERPOLATOR - captureSmartspace = !viewModel.useLargeClock + captureSmartspace = !viewModel.useLargeClock && smartspaceViewModel.isSmartspaceEnabled if (viewModel.useLargeClock) { viewModel.clock?.let { it.largeClock.layout.views.forEach { addTarget(it) } } @@ -252,11 +255,12 @@ class ClockSizeTransition( class ClockFaceOutTransition( config: IntraBlueprintTransition.Config, val viewModel: KeyguardClockViewModel, + val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { init { duration = CLOCK_OUT_MILLIS interpolator = CLOCK_OUT_INTERPOLATOR - captureSmartspace = viewModel.useLargeClock + captureSmartspace = viewModel.useLargeClock && smartspaceViewModel.isSmartspaceEnabled if (viewModel.useLargeClock) { addTarget(R.id.lockscreen_clock_view) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index a3888c3341db..9fa14236ee77 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -102,7 +102,6 @@ constructor( override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.sharedFlow( duration = 500.milliseconds, - onStart = { 1f }, onStep = { 1f }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt index 85885b065264..9fc759b8eca1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt @@ -53,7 +53,6 @@ constructor( return transitionAnimation.sharedFlowWithState( startTime = 600.milliseconds, duration = 500.milliseconds, - onStart = { translatePx }, onStep = { translatePx + it * -translatePx }, onFinish = { 0f }, onCancel = { 0f }, @@ -66,7 +65,6 @@ constructor( transitionAnimation.sharedFlow( startTime = 700.milliseconds, duration = 400.milliseconds, - onStart = { 0f }, onStep = { it }, onFinish = { 1f }, onCancel = { 1f }, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt index c61b1f5cc3be..d7ba46b6e708 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt @@ -54,7 +54,6 @@ constructor( startTime = 233.milliseconds, duration = 250.milliseconds, onStep = { it }, - onStart = { 0f }, ) override val deviceEntryParentViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index 19c11a947fe2..3a19780c7017 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -78,7 +78,6 @@ constructor( startTime = 233.milliseconds, duration = 250.milliseconds, onStep = { it }, - onStart = { 0f }, name = "OCCLUDED->LOCKSCREEN: lockscreenAlpha", ) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt index 1b14f75d54ef..898eacff6246 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt @@ -25,8 +25,9 @@ import android.widget.SeekBar import android.widget.TextView import androidx.constraintlayout.widget.Barrier import com.android.internal.widget.CachingIconView -import com.android.systemui.res.R import com.android.systemui.media.controls.models.GutsViewHolder +import com.android.systemui.res.R +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView import com.android.systemui.surfaceeffects.ripple.MultiRippleView import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView import com.android.systemui.util.animation.TransitionLayout @@ -42,6 +43,7 @@ class MediaViewHolder constructor(itemView: View) { val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view) val turbulenceNoiseView = itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view) + val loadingEffectView = itemView.requireViewById<LoadingEffectView>(R.id.loading_effect_view) val appIcon = itemView.requireViewById<ImageView>(R.id.icon) val titleText = itemView.requireViewById<TextView>(R.id.header_title) val artistText = itemView.requireViewById<TextView>(R.id.header_artist) @@ -171,6 +173,7 @@ class MediaViewHolder constructor(itemView: View) { setOf( R.id.album_art, R.id.turbulence_noise_view, + R.id.loading_effect_view, R.id.touch_ripple_view, ) } 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 c87fd14a943d..952f9b8711f0 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 @@ -29,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.media.controls.models.player.MediaViewHolder import com.android.systemui.monet.ColorScheme +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect import com.android.systemui.surfaceeffects.ripple.MultiRippleController import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController @@ -118,6 +119,7 @@ internal constructor( turbulenceNoiseController, ::AnimatingColorTransition ) + var loadingEffect: LoadingEffect? = null val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20) val surfaceColor = @@ -128,7 +130,6 @@ internal constructor( mediaViewHolder.albumView.backgroundTintList = colorList mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor) } - val accentPrimary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorPrimary), @@ -139,6 +140,7 @@ internal constructor( mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary) multiRippleController.updateColor(accentPrimary) turbulenceNoiseController.updateNoiseColor(accentPrimary) + loadingEffect?.updateColor(accentPrimary) } val accentSecondary = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index aa92814a584d..e97c9d3d8c0b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -41,6 +41,7 @@ import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.BitmapDrawable; @@ -81,6 +82,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.widget.CachingIconView; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.ActivityIntentHelper; +import com.android.systemui.Flags; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.GhostedViewTransitionAnimatorController; import com.android.systemui.bluetooth.BroadcastDialogController; @@ -111,6 +113,9 @@ import com.android.systemui.res.R; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState; +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView; import com.android.systemui.surfaceeffects.ripple.MultiRippleController; import com.android.systemui.surfaceeffects.ripple.MultiRippleView; import com.android.systemui.surfaceeffects.ripple.RippleAnimation; @@ -248,13 +253,34 @@ public class MediaControlPanel { private String mCurrentBroadcastApp; private MultiRippleController mMultiRippleController; private TurbulenceNoiseController mTurbulenceNoiseController; + private LoadingEffect mLoadingEffect; private final GlobalSettings mGlobalSettings; - + private final Random mRandom = new Random(); private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; private boolean mWasPlaying = false; private boolean mButtonClicked = false; - private final Random mRandom = new Random(); + private final LoadingEffect.Companion.PaintDrawCallback mNoiseDrawCallback = + new LoadingEffect.Companion.PaintDrawCallback() { + @Override + public void onDraw(@NonNull Paint loadingPaint) { + mMediaViewHolder.getLoadingEffectView().draw(loadingPaint); + } + }; + private final LoadingEffect.Companion.AnimationStateChangedCallback mStateChangedCallback = + new LoadingEffect.Companion.AnimationStateChangedCallback() { + @Override + public void onStateChanged(@NonNull AnimationState oldState, + @NonNull AnimationState newState) { + LoadingEffectView loadingEffectView = + mMediaViewHolder.getLoadingEffectView(); + if (newState == AnimationState.NOT_PLAYING) { + loadingEffectView.setVisibility(View.INVISIBLE); + } else { + loadingEffectView.setVisibility(View.VISIBLE); + } + } + }; /** * Initialize a new control panel @@ -456,6 +482,10 @@ public class MediaControlPanel { TurbulenceNoiseView turbulenceNoiseView = vh.getTurbulenceNoiseView(); turbulenceNoiseView.setBlendMode(BlendMode.SCREEN); + LoadingEffectView loadingEffectView = vh.getLoadingEffectView(); + loadingEffectView.setBlendMode(BlendMode.SCREEN); + loadingEffectView.setVisibility(View.INVISIBLE); + mTurbulenceNoiseController = new TurbulenceNoiseController(turbulenceNoiseView); mColorSchemeTransition = new ColorSchemeTransition( @@ -587,22 +617,41 @@ public class MediaControlPanel { } } - // Turbulence noise if (shouldPlayTurbulenceNoise()) { + // Need to create the config here to get the correct view size and color. if (mTurbulenceNoiseAnimationConfig == null) { mTurbulenceNoiseAnimationConfig = - createTurbulenceNoiseAnimation(); + createTurbulenceNoiseConfig(); + } + + if (Flags.shaderlibLoadingEffectRefactor()) { + if (mLoadingEffect == null) { + mLoadingEffect = new LoadingEffect( + Type.SIMPLEX_NOISE, + mTurbulenceNoiseAnimationConfig, + mNoiseDrawCallback, + mStateChangedCallback + ); + mColorSchemeTransition.setLoadingEffect(mLoadingEffect); + } + + mLoadingEffect.play(); + mMainExecutor.executeDelayed( + mLoadingEffect::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); + } else { + mTurbulenceNoiseController.play( + Type.SIMPLEX_NOISE, + mTurbulenceNoiseAnimationConfig + ); + mMainExecutor.executeDelayed( + mTurbulenceNoiseController::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); } - // Color will be correctly updated in ColorSchemeTransition. - mTurbulenceNoiseController.play( - Type.SIMPLEX_NOISE, - mTurbulenceNoiseAnimationConfig - ); - mMainExecutor.executeDelayed( - mTurbulenceNoiseController::finish, - TURBULENCE_NOISE_PLAY_DURATION - ); } + mButtonClicked = false; mWasPlaying = isPlaying(); @@ -1232,7 +1281,13 @@ public class MediaControlPanel { return mButtonClicked && !mWasPlaying && isPlaying(); } - private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() { + private TurbulenceNoiseAnimationConfig createTurbulenceNoiseConfig() { + View targetView = Flags.shaderlibLoadingEffectRefactor() + ? mMediaViewHolder.getLoadingEffectView() : + mMediaViewHolder.getTurbulenceNoiseView(); + int width = targetView.getWidth(); + int height = targetView.getHeight(); + return new TurbulenceNoiseAnimationConfig( /* gridCount= */ 2.14f, TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, @@ -1242,10 +1297,11 @@ public class MediaControlPanel { /* noiseMoveSpeedX= */ 0.42f, /* noiseMoveSpeedY= */ 0f, TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, + // Color will be correctly updated in ColorSchemeTransition. /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), /* backgroundColor= */ Color.BLACK, - /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(), - /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(), + width, + height, TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, /* easeInDuration= */ 1350f, /* easeOutDuration= */ 1350f, diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index 25d89fac1af5..02be0c1a6c2d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -35,10 +35,10 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import javax.inject.Inject -/** - * Factory to create [MediaOutputDialog] objects. - */ -open class MediaOutputDialogFactory @Inject constructor( +/** Factory to create [MediaOutputDialog] objects. */ +open class MediaOutputDialogFactory +@Inject +constructor( private val context: Context, private val mediaSessionManager: MediaSessionManager, private val lbm: LocalBluetoothManager?, @@ -55,46 +55,93 @@ open class MediaOutputDialogFactory @Inject constructor( private val userTracker: UserTracker ) { companion object { - private const val INTERACTION_JANK_TAG = "media_output" + const val INTERACTION_JANK_TAG = "media_output" var mediaOutputDialog: MediaOutputDialog? = null } /** Creates a [MediaOutputDialog] for the given package. */ open fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) { - create(packageName, aboveStatusBar, view, includePlaybackAndAppMetadata = true) + createWithController( + packageName, + aboveStatusBar, + controller = + view?.let { + DialogTransitionAnimator.Controller.fromView( + it, + DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) + }, + ) } - open fun createDialogForSystemRouting() { - create(packageName = null, aboveStatusBar = false, includePlaybackAndAppMetadata = false) + /** Creates a [MediaOutputDialog] for the given package. */ + open fun createWithController( + packageName: String, + aboveStatusBar: Boolean, + controller: DialogTransitionAnimator.Controller?, + ) { + create( + packageName, + aboveStatusBar, + dialogTransitionAnimatorController = controller, + includePlaybackAndAppMetadata = true + ) + } + + open fun createDialogForSystemRouting(controller: DialogTransitionAnimator.Controller? = null) { + create( + packageName = null, + aboveStatusBar = false, + dialogTransitionAnimatorController = null, + includePlaybackAndAppMetadata = false + ) } private fun create( - packageName: String?, - aboveStatusBar: Boolean, - view: View? = null, - includePlaybackAndAppMetadata: Boolean = true + packageName: String?, + aboveStatusBar: Boolean, + dialogTransitionAnimatorController: DialogTransitionAnimator.Controller?, + includePlaybackAndAppMetadata: Boolean = true ) { // Dismiss the previous dialog, if any. mediaOutputDialog?.dismiss() - val controller = MediaOutputController( - context, packageName, - mediaSessionManager, lbm, starter, notifCollection, - dialogTransitionAnimator, nearbyMediaDevicesManager, audioManager, - powerExemptionManager, keyGuardManager, featureFlags, userTracker) + val controller = + MediaOutputController( + context, + packageName, + mediaSessionManager, + lbm, + starter, + notifCollection, + dialogTransitionAnimator, + nearbyMediaDevicesManager, + audioManager, + powerExemptionManager, + keyGuardManager, + featureFlags, + userTracker + ) val dialog = - MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller, - dialogTransitionAnimator, uiEventLogger, includePlaybackAndAppMetadata) + MediaOutputDialog( + context, + aboveStatusBar, + broadcastSender, + controller, + dialogTransitionAnimator, + uiEventLogger, + includePlaybackAndAppMetadata + ) mediaOutputDialog = dialog // Show the dialog. - if (view != null) { - dialogTransitionAnimator.showFromView( - dialog, view, - cuj = DialogCuj( - InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, - INTERACTION_JANK_TAG - ) + if (dialogTransitionAnimatorController != null) { + dialogTransitionAnimator.show( + dialog, + dialogTransitionAnimatorController, ) } else { dialog.show() diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 092f1ed7d498..152f193be3f9 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -44,6 +44,7 @@ import android.view.WindowManagerGlobal; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.settingslib.applications.InterestingConfigChanges; @@ -89,7 +90,16 @@ public class NavigationBarControllerImpl implements private final TaskbarDelegate mTaskbarDelegate; private final NavBarHelper mNavBarHelper; private int mNavMode; + /** + * Indicates whether the active display is a large screen, e.g. tablets, foldable devices in + * the unfolded state. + */ @VisibleForTesting boolean mIsLargeScreen; + /** + * Indicates whether the device is a phone, rather than everything else (e.g. foldables, + * tablets) is considered not a handheld device. + */ + @VisibleForTesting boolean mIsPhone; /** A displayId - nav bar maps. */ @VisibleForTesting @@ -139,6 +149,8 @@ public class NavigationBarControllerImpl implements dumpManager, autoHideController, lightBarController, pipOptional, backAnimation.orElse(null), taskStackChangeListeners); mIsLargeScreen = isLargeScreen(mContext); + mIsPhone = + mContext.getResources().getIntArray(R.array.config_foldedDeviceStates).length == 0; dumpManager.registerDumpable(this); } @@ -253,9 +265,8 @@ public class NavigationBarControllerImpl implements /** @return {@code true} if taskbar is enabled, false otherwise */ private boolean initializeTaskbarIfNecessary() { - // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen - boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification()) - && shouldCreateNavBarAndTaskBar(mContext.getDisplayId()); + boolean taskbarEnabled = supportsTaskbar() && shouldCreateNavBarAndTaskBar( + mContext.getDisplayId()); if (taskbarEnabled) { Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary"); @@ -274,6 +285,12 @@ public class NavigationBarControllerImpl implements return taskbarEnabled; } + @VisibleForTesting + boolean supportsTaskbar() { + // Enable for tablets, unfolded state on a foldable device or (non handheld AND flag is set) + return mIsLargeScreen || (!mIsPhone && enableTaskbarNavbarUnification()); + } + private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() { @Override public void onDisplayRemoved(int displayId) { diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java index 3671dd439239..b4cc196b89ed 100644 --- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java @@ -39,7 +39,10 @@ public class ProcessWrapper { /** * Returns {@link UserHandle} as returned statically by {@link Process#myUserHandle()}. * - * Please strongly consider using {@link com.android.systemui.settings.UserTracker} instead. + * This should not be used to get the "current" user. This information only applies to the + * current process, not the current state of SystemUI. Please use + * {@link com.android.systemui.settings.UserTracker} if you want to learn about the currently + * active user in SystemUI. */ public UserHandle myUserHandle() { return Process.myUserHandle(); diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index a755805d1872..4ccb18fced56 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -19,15 +19,16 @@ package com.android.systemui.scene.shared.flag import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR +import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.Flags.keyguardBottomAreaRefactor +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.Flags.sceneContainer import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FlagToken import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED import com.android.systemui.flags.RefactorFlagUtils -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.media.controls.util.MediaInSceneContainerFlag import dagger.Module import dagger.Provides @@ -43,7 +44,7 @@ object SceneContainerFlag { SCENE_CONTAINER_ENABLED && // mainStaticFlag sceneContainer() && // mainAconfigFlag keyguardBottomAreaRefactor() && - KeyguardShadeMigrationNssl.isEnabled && + migrateClocksToBlueprint() && MediaInSceneContainerFlag.isEnabled && // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer ComposeFacade.isComposeAvailable() @@ -63,7 +64,7 @@ object SceneContainerFlag { inline fun getSecondaryFlags(): Sequence<FlagToken> = sequenceOf( FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()), - KeyguardShadeMigrationNssl.token, + FlagToken(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, migrateClocksToBlueprint()), MediaInSceneContainerFlag.token, // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer ) diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 2f0fc5127009..ee602e5f1c04 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -23,6 +23,7 @@ import android.content.DialogInterface.BUTTON_NEGATIVE import android.content.DialogInterface.BUTTON_POSITIVE import android.content.Intent import android.content.Intent.EXTRA_PACKAGE_NAME +import android.content.pm.PackageManager import android.hardware.SensorPrivacyManager import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS import android.hardware.SensorPrivacyManager.EXTRA_SENSOR @@ -31,6 +32,7 @@ import android.os.Bundle import android.os.Handler import android.window.OnBackInvokedDispatcher import androidx.annotation.OpenForTesting +import com.android.internal.camera.flags.Flags import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE @@ -90,14 +92,14 @@ open class SensorUseStartedActivity @Inject constructor( sensor = ALL_SENSORS val callback = IndividualSensorPrivacyController.Callback { _, _ -> if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) && - !sensorPrivacyController.isSensorBlocked(CAMERA)) { + !isCameraBlocked(sensorUsePackageName)) { finish() } } sensorPrivacyListener = callback sensorPrivacyController.addCallback(callback) if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) && - !sensorPrivacyController.isSensorBlocked(CAMERA)) { + !isCameraBlocked(sensorUsePackageName)) { finish() return } @@ -110,14 +112,22 @@ open class SensorUseStartedActivity @Inject constructor( } val callback = IndividualSensorPrivacyController.Callback { whichSensor: Int, isBlocked: Boolean -> - if (whichSensor == sensor && !isBlocked) { + if (whichSensor != sensor) { + // Ignore a callback; we're not interested in. + } else if ((whichSensor == CAMERA) && !isCameraBlocked(sensorUsePackageName)) { + finish() + } else if ((whichSensor == MICROPHONE) && !isBlocked) { finish() } } sensorPrivacyListener = callback sensorPrivacyController.addCallback(callback) - if (!sensorPrivacyController.isSensorBlocked(sensor)) { + if ((sensor == CAMERA) && !isCameraBlocked(sensorUsePackageName)) { + finish() + return + } else if ((sensor == MICROPHONE) && + !sensorPrivacyController.isSensorBlocked(MICROPHONE)) { finish() return } @@ -204,6 +214,22 @@ open class SensorUseStartedActivity @Inject constructor( recreate() } + private fun isAutomotive(): Boolean { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + } + + private fun isCameraBlocked(packageName: String): Boolean { + if (Flags.cameraPrivacyAllowlist()) { + if (isAutomotive()) { + return sensorPrivacyController.isCameraPrivacyEnabled(packageName) + } else { + return sensorPrivacyController.isSensorBlocked(CAMERA) + } + } else { + return sensorPrivacyController.isSensorBlocked(CAMERA) + } + } + private fun disableSensorPrivacy() { if (sensor == ALL_SENSORS) { sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 0e359b7f0eec..9a03393be979 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -71,7 +71,6 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; import android.view.HapticFeedbackConstants; -import android.view.InputDevice; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -137,7 +136,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver; import com.android.systemui.keyguard.shared.ComposeLockscreen; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; @@ -1022,7 +1020,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump instantCollapse(); } else { mView.animate().cancel(); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mView.animate() .alpha(0f) .setStartDelay(0) @@ -1158,7 +1156,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Occluded->Lockscreen collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), mOccludedToLockscreenTransition, mMainDispatcher); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); collectFlow(mView, @@ -1169,7 +1167,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Lockscreen->Dreaming collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), mLockscreenToDreamingTransition, mMainDispatcher); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); @@ -1181,7 +1179,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Gone->Dreaming collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(), mGoneToDreamingTransition, mMainDispatcher); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); } @@ -1192,7 +1190,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Lockscreen->Occluded collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), mLockscreenToOccludedTransition, mMainDispatcher); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(), @@ -1200,7 +1198,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } // Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth) - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); } @@ -1278,7 +1276,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.onDestroy(); } - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { // Need a shared controller until mKeyguardStatusViewController can be removed from // here, due to important state being set in that controller. Rebind in order to pick // up config changes @@ -1334,7 +1332,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Reset any left over overscroll state. It is a rare corner case but can happen. mQsController.setOverScrollAmount(0); mScrimController.setNotificationsOverScrollAmount(0); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mNotificationStackScrollLayoutController.setOverExpansion(0); mNotificationStackScrollLayoutController.setOverScrollAmount(0); } @@ -1355,7 +1353,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } updateClockAppearance(); mQsController.updateQsState(); - if (!KeyguardShadeMigrationNssl.isEnabled() && !FooterViewRefactor.isEnabled()) { + if (!migrateClocksToBlueprint() && !FooterViewRefactor.isEnabled()) { mNotificationStackScrollLayoutController.updateFooter(); } } @@ -1387,7 +1385,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump void reInflateViews() { debugLog("reInflateViews"); // Re-inflate the status view group. - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { KeyguardStatusView keyguardStatusView = mNotificationContainerParent.findViewById(R.id.keyguard_status_view); int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView); @@ -1507,7 +1505,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void updateMaxDisplayedNotifications(boolean recompute) { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { return; } @@ -1664,7 +1662,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), mKeyguardStatusViewController.isClockTopAligned()); mClockPositionAlgorithm.run(mClockPositionResult); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.setLockscreenClockY( mClockPositionAlgorithm.getExpandedPreferredClockY()); } @@ -1678,7 +1676,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange; - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.updatePosition( mClockPositionResult.clockX, mClockPositionResult.clockY, mClockPositionResult.clockScale, animateClock); @@ -1754,7 +1752,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void updateKeyguardStatusViewAlignment(boolean animate) { boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); ConstraintLayout layout; - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { layout = mKeyguardViewConfigurator.getKeyguardRootView(); } else { layout = mNotificationContainerParent; @@ -1930,7 +1928,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha; mKeyguardStatusViewController.setAlpha(alpha); - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { // TODO (b/296373478) This is for split shade media movement. } else { mKeyguardStatusViewController @@ -2523,7 +2521,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump void requestScrollerTopPaddingUpdate(boolean animate) { float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing, getKeyguardNotificationStaticPadding(), mExpandedFraction); - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { mSharedNotificationContainerInteractor.setTopPosition(padding); } else { mNotificationStackScrollLayoutController.updateTopPadding(padding, animate); @@ -2705,7 +2703,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return; } - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { float alpha = 1f; if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) { @@ -2740,7 +2738,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void updateKeyguardBottomAreaAlpha() { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { return; } if (mIsOcclusionTransitionRunning) { @@ -2981,7 +2979,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void onScreenTurningOn() { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.dozeTimeTick(); } } @@ -3233,7 +3231,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump public void dozeTimeTick() { mLockIconViewController.dozeTimeTick(); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.dozeTimeTick(); } if (mInterpolatedDarkAmount > 0) { @@ -4449,7 +4447,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump && statusBarState == KEYGUARD) { // This means we're doing the screen off animation - position the keyguard status // view where it'll be on AOD, so we can animate it in. - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.updatePosition( mClockPositionResult.clockX, mClockPositionResult.clockYFullyDozing, @@ -4569,7 +4567,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump setDozing(true /* dozing */, false /* animate */); mStatusBarStateController.setUpcomingState(KEYGUARD); - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { mStatusBarStateController.setState(KEYGUARD); } else { mStatusBarStateListener.onStateChanged(KEYGUARD); @@ -4630,7 +4628,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); // Update Clock Pivot (used by anti-burnin transformations) - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight()); } @@ -4740,7 +4738,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private Consumer<Float> setTransitionY( NotificationStackScrollLayoutController stackScroller) { return (Float translationY) -> { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false); stackScroller.setTranslationY(translationY); @@ -4782,7 +4780,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { - if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) { + if (migrateClocksToBlueprint() && !mUseExternalTouch) { return false; } @@ -4853,7 +4851,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mCentralSurfaces.userActivity(); } mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation; @@ -4954,7 +4952,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump */ @Override public boolean onTouchEvent(MotionEvent event) { - if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) { + if (migrateClocksToBlueprint() && !mUseExternalTouch) { return false; } @@ -5066,19 +5064,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return false; } - final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll( - mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe( - mTrackpadGestureFeaturesEnabled, event); - - // On expanding, single mouse click expands the panel instead of dragging. - if (isFullyCollapsed() && (event.isFromSource(InputDevice.SOURCE_MOUSE) - && !isTrackpadTwoOrThreeFingerSwipe)) { - if (event.getAction() == MotionEvent.ACTION_UP) { - expand(true /* animate */); - } - return true; - } - /* * We capture touch events here and update the expand height here in case according to * the users fingers. This also handles multi-touch. @@ -5099,6 +5084,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mIgnoreXTouchSlop = true; } + final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll( + mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe( + mTrackpadGestureFeaturesEnabled, event); + switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index aa2d606c5126..99e91c1d332f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.shade; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; @@ -48,7 +49,6 @@ import com.android.systemui.flags.Flags; import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder; @@ -320,7 +320,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mTouchActive = true; mTouchCancelled = false; mDownEvent = ev; - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { mService.userActivity(); } } else if (ev.getActionMasked() == MotionEvent.ACTION_UP @@ -475,7 +475,7 @@ public class NotificationShadeWindowViewController implements Dumpable { && !bouncerShowing && !mStatusBarStateController.isDozing()) { if (mDragDownHelper.isDragDownEnabled()) { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { // When on lockscreen, if the touch originates at the top of the screen // go directly to QS and not the shade if (mStatusBarStateController.getState() == KEYGUARD @@ -488,7 +488,7 @@ public class NotificationShadeWindowViewController implements Dumpable { // This handles drag down over lockscreen boolean result = mDragDownHelper.onInterceptTouchEvent(ev); - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { if (result) { mLastInterceptWasDragDownHelper = true; if (ev.getAction() == MotionEvent.ACTION_DOWN) { @@ -520,7 +520,7 @@ public class NotificationShadeWindowViewController implements Dumpable { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); mStackScrollLayout.onInterceptTouchEvent(cancellation); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mNotificationPanelViewController.handleExternalInterceptTouch(cancellation); } cancellation.recycle(); @@ -535,7 +535,7 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarKeyguardViewManager.onTouch(ev)) { return true; } - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) { // we still want to finish our drag down gesture when locking the screen handled |= mDragDownHelper.onTouchEvent(ev) || handled; @@ -625,7 +625,7 @@ public class NotificationShadeWindowViewController implements Dumpable { } private boolean didNotificationPanelInterceptEvent(MotionEvent ev) { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need // to also ask NotificationPanelViewController directly, in order to process swipe up // events originating from notifications diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index c0afa32571e7..457b3d7e6217 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -28,10 +28,10 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.lifecycle.lifecycleScope import com.android.systemui.Flags.centralizedStatusBarDimensRefactor +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.fragments.FragmentService -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.plugins.qs.QS @@ -284,7 +284,7 @@ class NotificationsQSContainerController @Inject constructor( } private fun setNotificationsConstraints(constraintSet: ConstraintSet) { - if (KeyguardShadeMigrationNssl.isEnabled) { + if (migrateClocksToBlueprint()) { return } val startConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java index 25e558ee42dd..e82f2d3cbd30 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java @@ -18,6 +18,8 @@ package com.android.systemui.shade; import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAPH; +import static com.android.systemui.Flags.migrateClocksToBlueprint; + import android.app.Fragment; import android.content.Context; import android.content.res.Configuration; @@ -33,7 +35,6 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.plugins.qs.QS; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AboveShelfObserver; @@ -189,7 +190,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (KeyguardShadeMigrationNssl.isEnabled()) { + if (migrateClocksToBlueprint()) { return super.drawChild(canvas, child, drawingTime); } int layoutIndex = mLayoutDrawingOrder.indexOf(child); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index f3e9c7503626..f86c71b9508c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -21,6 +21,7 @@ import static android.view.WindowInsets.Type.ime; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS; import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE; @@ -70,7 +71,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; @@ -1782,7 +1782,7 @@ public class QuickSettingsController implements Dumpable { // Dragging down on the lockscreen statusbar should prohibit other interactions // immediately, otherwise we'll wait on the touchslop. This is to allow // dragging down to expanded quick settings directly on the lockscreen. - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mPanelView.getParent().requestDisallowInterceptTouchEvent(true); } } @@ -1827,7 +1827,7 @@ public class QuickSettingsController implements Dumpable { && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldQuickSettingsIntercept( mInitialTouchX, mInitialTouchY, h)) { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mPanelView.getParent().requestDisallowInterceptTouchEvent(true); } mShadeLog.onQsInterceptMoveQsTrackingEnabled(h); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index 7cb3be7f159d..6a2a6a417f5a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -241,7 +241,7 @@ constructor( } override fun onStatusBarTouch(event: MotionEvent) { - // The only call to this doesn't happen with KeyguardShadeMigrationNssl enabled + // The only call to this doesn't happen with migrateClocksToBlueprint() enabled throw UnsupportedOperationException() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 14230ba43f59..19fe60a60bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import static android.adaptiveauth.Flags.enableAdaptiveAuth; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE; @@ -32,6 +33,7 @@ import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ADAPTIVE_AUTH; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; @@ -454,6 +456,9 @@ public class KeyguardIndicationController { updateLockScreenAlignmentMsg(); updateLockScreenLogoutView(); updateLockScreenPersistentUnlockMsg(); + if (enableAdaptiveAuth()) { + updateLockScreenAdaptiveAuthMsg(userId); + } } private void updateOrganizedOwnedDevice() { @@ -740,6 +745,22 @@ public class KeyguardIndicationController { } } + private void updateLockScreenAdaptiveAuthMsg(int userId) { + final boolean deviceLocked = mKeyguardUpdateMonitor.isDeviceLockedByAdaptiveAuth(userId); + if (deviceLocked) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_ADAPTIVE_AUTH, + new KeyguardIndication.Builder() + .setMessage(mContext + .getString(R.string.kg_prompt_after_adaptive_auth_lock)) + .setTextColor(mInitialTextColorState) + .build(), + true); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_ADAPTIVE_AUTH); + } + } + private boolean isOrganizationOwnedDevice() { return mDevicePolicyManager.isDeviceManaged() || mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index ef5026538216..2e7110381b91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Flags.nsslFalsingFix +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.Gefingerpoken import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy import com.android.systemui.classifier.Classifier @@ -23,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll import com.android.systemui.plugins.ActivityStarter @@ -890,7 +890,7 @@ class DragDownHelper( isDraggingDown = false isTrackpadReverseScroll = false shadeRepository.setLegacyLockscreenShadeTracking(false) - if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled) { + if (nsslFalsingFix() || migrateClocksToBlueprint()) { return true } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 2a4753def463..9916ef6ff9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -749,7 +749,7 @@ public class NotificationLockscreenUserManagerImpl implements || isNotifUserRedacted; boolean notificationRequestsRedaction = - ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; + ent.isNotificationVisibilityPrivate(); boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey()); if (keyguardPrivateNotifications()) { @@ -767,9 +767,7 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { - return entry != null && entry.getRanking().getChannel() != null - && entry.getRanking().getChannel().getLockscreenVisibility() - == Notification.VISIBILITY_PRIVATE; + return entry != null && entry.isChannelVisibilityPrivate(); } else { return entry != null && entry.getRanking().getLockscreenVisibilityOverride() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 8678f0aad181..e111525285e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -998,6 +998,23 @@ public final class NotificationEntry extends ListEntry { return style == null ? "nostyle" : style.getSimpleName(); } + /** + * Return {@code true} if notification's visibility is {@link Notification.VISIBILITY_PRIVATE} + */ + public boolean isNotificationVisibilityPrivate() { + return getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; + } + + /** + * Return {@code true} if notification's channel lockscreen visibility is + * {@link Notification.VISIBILITY_PRIVATE} + */ + public boolean isChannelVisibilityPrivate() { + return getRanking().getChannel() != null + && getRanking().getChannel().getLockscreenVisibility() + == Notification.VISIBILITY_PRIVATE; + } + /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 54b6ad71e734..fb67f7cc0b0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -104,7 +104,7 @@ class HeadsUpCoordinator @Inject constructor( /** * Once the pipeline starts running, we can look through posted entries and quickly process - * any that don't have groups, and thus will never gave a group alert edge case. + * any that don't have groups, and thus will never gave a group heads up edge case. */ fun onBeforeTransformGroups(list: List<ListEntry>) { mNow = mSystemClock.currentTimeMillis() @@ -125,7 +125,7 @@ class HeadsUpCoordinator @Inject constructor( /** * Once we have a nearly final shade list (not including what's pruned for inflation reasons), * we know that stability and [NotifPromoter]s have been applied, so we can use the location of - * notifications in this list to determine what kind of group alert behavior should happen. + * notifications in this list to determine what kind of group heads up behavior should happen. */ fun onBeforeFinalizeFilter(list: List<ListEntry>) = mHeadsUpManager.modifyHuns { hunMutator -> // Nothing to do if there are no other adds/updates @@ -140,7 +140,7 @@ class HeadsUpCoordinator @Inject constructor( .groupBy { it.sbn.groupKey } val groupLocationsByKey: Map<String, GroupLocation> by lazy { getGroupLocationsByKey(list) } mLogger.logEvaluatingGroups(postedEntriesByGroup.size) - // For each group, determine which notification(s) for a group should alert. + // For each group, determine which notification(s) for a group should heads up. postedEntriesByGroup.forEach { (groupKey, postedEntries) -> // get and classify the logical members val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList() @@ -149,7 +149,7 @@ class HeadsUpCoordinator @Inject constructor( // Report the start of this group's evaluation mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size) - // If there is no logical summary, then there is no alert to transfer + // If there is no logical summary, then there is no heads up to transfer if (logicalSummary == null) { postedEntries.forEach { handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing") @@ -157,43 +157,43 @@ class HeadsUpCoordinator @Inject constructor( return@forEach } - // If summary isn't wanted to be heads up, then there is no alert to transfer + // If summary isn't wanted to be heads up, then there is no heads up to transfer if (!isGoingToShowHunStrict(logicalSummary)) { postedEntries.forEach { - handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-alerting") + handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-heads-up") } return@forEach } - // The group is alerting! Overall goals: - // - Maybe transfer its alert to a child - // - Also let any/all newly alerting children still alert - var childToReceiveParentAlert: NotificationEntry? + // The group is heads up! Overall goals: + // - Maybe transfer its heads up to a child + // - Also let any/all newly heads up children still heads up + var childToReceiveParentHeadsUp: NotificationEntry? var targetType = "undefined" - // If the parent is alerting, always look at the posted notification with the newest + // If the parent is heads up, always look at the posted notification with the newest // 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive the - // parent's alert. - childToReceiveParentAlert = - findAlertOverride(postedEntries, groupLocationsByKey::getLocation) - if (childToReceiveParentAlert != null) { - targetType = "alertOverride" + // parent's heads up. + childToReceiveParentHeadsUp = + findHeadsUpOverride(postedEntries, groupLocationsByKey::getLocation) + if (childToReceiveParentHeadsUp != null) { + targetType = "headsUpOverride" } - // If the summary is Detached and we have not picked a receiver of the alert, then we - // need to look for the best child to alert in place of the summary. + // If the summary is Detached and we have not picked a receiver of the heads up, then we + // need to look for the best child to heads up in place of the summary. val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key) - if (!isSummaryAttached && childToReceiveParentAlert == null) { - childToReceiveParentAlert = + if (!isSummaryAttached && childToReceiveParentHeadsUp == null) { + childToReceiveParentHeadsUp = findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation) - if (childToReceiveParentAlert != null) { + if (childToReceiveParentHeadsUp != null) { targetType = "bestChild" } } - // If there is no child to receive the parent alert, then just handle the posted entries - // and return. - if (childToReceiveParentAlert == null) { + // If there is no child to receive the parent heads up, then just handle the posted + // entries and return. + if (childToReceiveParentHeadsUp == null) { postedEntries.forEach { handlePostedEntry(it, hunMutator, scenario = "no-transfer-target") } @@ -203,14 +203,14 @@ class HeadsUpCoordinator @Inject constructor( // At this point we just need to initiate the transfer val summaryUpdate = mPostedEntries[logicalSummary.key] - // Because we now know for certain that some child is going to alert for this summary - // (as we have found a child to transfer the alert to), mark the group as having + // Because we now know for certain that some child is going to heads up for this summary + // (as we have found a child to transfer the heads up to), mark the group as having // interrupted. This will allow us to know in the future that the "should heads up" // state of this group has already been handled, just not via the summary entry itself. logicalSummary.setInterruption() - mLogger.logSummaryMarkedInterrupted(logicalSummary.key, childToReceiveParentAlert.key) + mLogger.logSummaryMarkedInterrupted(logicalSummary.key, childToReceiveParentHeadsUp.key) - // If the summary was not attached, then remove the alert from the detached summary. + // If the summary was not attached, then remove the heads up from the detached summary. // Otherwise we can simply ignore its posted update. if (!isSummaryAttached) { val summaryUpdateForRemoval = summaryUpdate?.also { @@ -221,60 +221,63 @@ class HeadsUpCoordinator @Inject constructor( wasUpdated = false, shouldHeadsUpEver = false, shouldHeadsUpAgain = false, - isAlerting = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key), + isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key), isBinding = isEntryBinding(logicalSummary), ) - // If we transfer the alert and the summary isn't even attached, that means we - // should ensure the summary is no longer alerting, so we remove it here. + // If we transfer the heads up notification and the summary isn't even attached, + // that means we should ensure the summary is no longer a heads up notification, + // so we remove it here. handlePostedEntry( summaryUpdateForRemoval, hunMutator, - scenario = "detached-summary-remove-alert") + scenario = "detached-summary-remove-heads-up") } else if (summaryUpdate != null) { mLogger.logPostedEntryWillNotEvaluate( summaryUpdate, reason = "attached-summary-transferred") } - // Handle all posted entries -- if the child receiving the parent's alert is in the - // list, then set its flags to ensure it alerts. - var didAlertChildToReceiveParentAlert = false + // Handle all posted entries -- if the child receiving the parent's heads up is in the + // list, then set its flags to ensure it heads up. + var didHeadsUpChildToReceiveParentHeadsUp = false postedEntries.asSequence() .filter { it.key != logicalSummary.key } .forEach { postedEntry -> - if (childToReceiveParentAlert.key == postedEntry.key) { + if (childToReceiveParentHeadsUp.key == postedEntry.key) { // Update the child's posted update so that it postedEntry.shouldHeadsUpEver = true postedEntry.shouldHeadsUpAgain = true handlePostedEntry( postedEntry, hunMutator, - scenario = "child-alert-transfer-target-$targetType") - didAlertChildToReceiveParentAlert = true + scenario = "child-heads-up-transfer-target-$targetType") + didHeadsUpChildToReceiveParentHeadsUp = true } else { handlePostedEntry( postedEntry, hunMutator, - scenario = "child-alert-non-target") + scenario = "child-heads-up-non-target") } } - // If the child receiving the alert was not updated on this tick (which can happen in a - // standard alert transfer scenario), then construct an update so that we can apply it. - if (!didAlertChildToReceiveParentAlert) { + // If the child receiving the heads up notification was not updated on this tick + // (which can happen in a standard heads up transfer scenario), then construct an update + // so that we can apply it. + if (!didHeadsUpChildToReceiveParentHeadsUp) { val posted = PostedEntry( - childToReceiveParentAlert, + childToReceiveParentHeadsUp, wasAdded = false, wasUpdated = false, shouldHeadsUpEver = true, shouldHeadsUpAgain = true, - isAlerting = mHeadsUpManager.isHeadsUpEntry(childToReceiveParentAlert.key), - isBinding = isEntryBinding(childToReceiveParentAlert), + isHeadsUpEntry = + mHeadsUpManager.isHeadsUpEntry(childToReceiveParentHeadsUp.key), + isBinding = isEntryBinding(childToReceiveParentHeadsUp), ) handlePostedEntry( posted, hunMutator, - scenario = "non-posted-child-alert-transfer-target-$targetType") + scenario = "non-posted-child-heads-up-transfer-target-$targetType") } } // After this method runs, all posted entries should have been handled (or skipped). @@ -286,9 +289,9 @@ class HeadsUpCoordinator @Inject constructor( /** * Find the posted child with the newest when, and return it if it is isolated and has - * GROUP_ALERT_SUMMARY so that it can be alerted. + * GROUP_ALERT_SUMMARY so that it can be heads uped. */ - private fun findAlertOverride( + private fun findHeadsUpOverride( postedEntries: List<PostedEntry>, locationLookupByKey: (String) -> GroupLocation, ): NotificationEntry? = postedEntries.asSequence() @@ -344,16 +347,17 @@ class HeadsUpCoordinator @Inject constructor( } } else { if (posted.isHeadsUpAlready) { - // NOTE: This might be because we're alerting (i.e. tracked by HeadsUpManager) OR - // it could be because we're binding, and that will affect the next step. + // NOTE: This might be because we're showing heads up (i.e. tracked by + // HeadsUpManager) OR it could be because we're binding, and that will affect the + // next step. if (posted.shouldHeadsUpEver) { - // If alerting, we need to post an update. Otherwise we're still binding, - // and we can just let that finish. - if (posted.isAlerting) { + // If showing heads up, we need to post an update. Otherwise we're still + // binding, and we can just let that finish. + if (posted.isHeadsUpEntry) { hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain) } } else { - if (posted.isAlerting) { + if (posted.isHeadsUpEntry) { // We don't want this to be interrupting anymore, let's remove it hunMutator.removeNotification(posted.key, false /*removeImmediately*/) } else { @@ -408,7 +412,7 @@ class HeadsUpCoordinator @Inject constructor( wasUpdated = false, shouldHeadsUpEver = shouldHeadsUpEver, shouldHeadsUpAgain = true, - isAlerting = false, + isHeadsUpEntry = false, isBinding = false, ) @@ -418,21 +422,21 @@ class HeadsUpCoordinator @Inject constructor( /** * Notification could've updated to be heads up or not heads up. Even if it did update to - * heads up, if the notification specified that it only wants to alert once, don't heads + * heads up, if the notification specified that it only wants to heads up once, don't heads * up again. */ override fun onEntryUpdated(entry: NotificationEntry) { val shouldHeadsUpEver = mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt val shouldHeadsUpAgain = shouldHunAgain(entry) - val isAlerting = mHeadsUpManager.isHeadsUpEntry(entry.key) + val isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key) val isBinding = isEntryBinding(entry) val posted = mPostedEntries.compute(entry.key) { _, value -> value?.also { update -> update.wasUpdated = true update.shouldHeadsUpEver = shouldHeadsUpEver update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain - update.isAlerting = isAlerting + update.isHeadsUpEntry = isHeadsUpEntry update.isBinding = isBinding } ?: PostedEntry( entry, @@ -440,15 +444,15 @@ class HeadsUpCoordinator @Inject constructor( wasUpdated = true, shouldHeadsUpEver = shouldHeadsUpEver, shouldHeadsUpAgain = shouldHeadsUpAgain, - isAlerting = isAlerting, + isHeadsUpEntry = isHeadsUpEntry, isBinding = isBinding, ) } - // Handle cancelling alerts here, rather than in the OnBeforeFinalizeFilter, so that + // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so that // work can be done before the ShadeListBuilder is run. This prevents re-entrant // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager. if (posted?.shouldHeadsUpEver == false) { - if (posted.isAlerting) { + if (posted.isHeadsUpEntry) { // We don't want this to be interrupting anymore, let's remove it mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/) } else if (posted.isBinding) { @@ -462,7 +466,7 @@ class HeadsUpCoordinator @Inject constructor( } /** - * Stop alerting HUNs that are removed from the notification collection + * Stop showing as heads up once removed from the notification collection */ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { mPostedEntries.remove(entry.key) @@ -484,7 +488,7 @@ class HeadsUpCoordinator @Inject constructor( /** * Identify notifications whose heads-up state changes when the notification rankings are - * updated, and have those changed notifications alert if necessary. + * updated, and have those changed notifications heads up if necessary. * * This method will occur after any operations in onEntryAdded or onEntryUpdated, so any * handling of ranking changes needs to take into account that we may have just made a @@ -492,7 +496,7 @@ class HeadsUpCoordinator @Inject constructor( */ override fun onRankingApplied() { // Because a ranking update may cause some notifications that are no longer (or were - // never) in mPostedEntries to need to alert, we need to check every notification + // never) in mPostedEntries to need to heads up, we need to check every notification // known to the pipeline. for (entry in mNotifPipeline.allNotifs) { // Only consider entries that are recent enough, since we want to apply a fairly @@ -500,9 +504,9 @@ class HeadsUpCoordinator @Inject constructor( // app-provided notification update. if (!isNewEnoughForRankingUpdate(entry)) continue - // The only entries we consider alerting for here are entries that have never - // interrupted and that now say they should heads up or FSI; if they've alerted in - // the past, we don't want to incorrectly alert a second time if there wasn't an + // The only entries we consider heads up for here are entries that have never + // interrupted and that now say they should heads up or FSI; if they've heads uped in + // the past, we don't want to incorrectly heads up a second time if there wasn't an // explicit notification update. if (entry.hasInterrupted()) continue @@ -561,7 +565,7 @@ class HeadsUpCoordinator @Inject constructor( } /** - * Checks whether an update for a notification warrants an alert for the user. + * Checks whether an update for a notification warrants an heads up for the user. */ private fun shouldHunAgain(entry: NotificationEntry): Boolean { return (!entry.hasInterrupted() || @@ -716,25 +720,25 @@ class HeadsUpCoordinator @Inject constructor( } /** - * Whether the notification is already alerting or binding so that it can imminently alert + * Whether the notification is already heads up or binding so that it can imminently heads up */ private fun isAttemptingToShowHun(entry: ListEntry) = mHeadsUpManager.isHeadsUpEntry(entry.key) || isEntryBinding(entry) /** - * Whether the notification is already alerting/binding per [isAttemptingToShowHun] OR if it - * has been updated so that it should alert this update. This method is permissive because it - * returns `true` even if the update would (in isolation of its group) cause the alert to be - * retracted. This is important for not retracting transferred group alerts. + * Whether the notification is already heads up/binding per [isAttemptingToShowHun] OR if it + * has been updated so that it should heads up this update. This method is permissive because + * it returns `true` even if the update would (in isolation of its group) cause the heads up to + * be retracted. This is important for not retracting transferred group heads ups. */ private fun isGoingToShowHunNoRetract(entry: ListEntry) = mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry) /** * If the notification has been updated, then whether it should HUN in isolation, otherwise - * defers to the already alerting/binding state of [isAttemptingToShowHun]. This method is - * strict because any update which would revoke the alert supersedes the current - * alerting/binding state. + * defers to the already heads up/binding state of [isAttemptingToShowHun]. This method is + * strict because any update which would revoke the heads up supersedes the current + * heads up/binding state. */ private fun isGoingToShowHunStrict(entry: ListEntry) = mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry) @@ -760,12 +764,12 @@ class HeadsUpCoordinator @Inject constructor( var wasUpdated: Boolean, var shouldHeadsUpEver: Boolean, var shouldHeadsUpAgain: Boolean, - var isAlerting: Boolean, + var isHeadsUpEntry: Boolean, var isBinding: Boolean, ) { val key = entry.key val isHeadsUpAlready: Boolean - get() = isAlerting || isBinding + get() = isHeadsUpEntry || isBinding val calculateShouldBeHeadsUpStrict: Boolean get() = shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain || isHeadsUpAlready) val calculateShouldBeHeadsUpNoRetract: Boolean @@ -781,7 +785,7 @@ private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation = /** * Invokes the given block with a [HunMutator] that defers all HUN removals. This ensures that the * HeadsUpManager is notified of additions before removals, which prevents a glitch where the - * HeadsUpManager temporarily believes that nothing is alerting, causing bad re-entrant behavior. + * HeadsUpManager temporarily believes that nothing is heads up, causing bad re-entrant behavior. */ private fun <R> HeadsUpManager.modifyHuns(block: (HunMutator) -> R): R { val mutator = HunMutatorImpl(this) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt index 94cc7e9d1a9f..f2b84827f3a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt @@ -76,6 +76,6 @@ class RowAppearanceCoordinator @Inject internal constructor( // Show/hide the feedback icon controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry)) // Show the "alerted" bell icon - controller.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs) + controller.setLastAudibleMs(entry.lastAudiblyAlertedMs) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt index 74ff78e25a2f..e8c59f44da0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt @@ -33,7 +33,7 @@ class LaunchFullScreenIntentProvider @Inject constructor() { private val listeners = ListenerSet<Listener>() /** - * Registers a listener with this provider. These listeners will be alerted whenever a full + * Registers a listener with this provider. These listeners will be updated whenever a full * screen intent should be launched for a notification entry. */ fun registerListener(listener: Listener) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt index 5ee94ba1624c..82b7e14adcc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt @@ -29,10 +29,10 @@ interface NotifRowController { fun setSystemExpanded(systemExpanded: Boolean) /** - * Sets the timestamp that the notification was last audibly alerted, which the row uses to + * Sets the timestamp that the notification was last audible, which the row uses to * show a bell icon in the header which indicates to the user which notification made a noise. */ - fun setLastAudiblyAlertedMs(lastAudiblyAlertedMs: Long) + fun setLastAudibleMs(lastAudibleMs: Long) /** Shows the given feedback icon, or hides the icon if null. */ fun setFeedbackIcon(icon: FeedbackIcon?) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 0afdefabd4f0..5614f3a3fcc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -431,8 +431,8 @@ public class ExpandableNotificationRowController implements NotifViewController } @Override - public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) { - mView.setLastAudiblyAlertedMs(lastAudiblyAlertedMs); + public void setLastAudibleMs(long lastAudibleMs) { + mView.setLastAudiblyAlertedMs(lastAudibleMs); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 20fae88b6f33..c90aceef6934 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -263,8 +263,8 @@ public class AmbientState implements Dumpable { return mStackHeight; } - /** Tracks the state from AlertingNotificationManager#hasNotifications() */ - private boolean mHasAlertEntries; + /** Tracks the state from HeadsUpManager#hasNotifications() */ + private boolean mHasHeadsUpEntries; @Inject public AmbientState( @@ -563,7 +563,7 @@ public class AmbientState implements Dumpable { } public boolean hasPulsingNotifications() { - return mPulsing && mHasAlertEntries; + return mPulsing && mHasHeadsUpEntries; } public void setPulsing(boolean hasPulsing) { @@ -716,8 +716,8 @@ public class AmbientState implements Dumpable { return mAppearFraction; } - public void setHasAlertEntries(boolean hasAlertEntries) { - mHasAlertEntries = hasAlertEntries; + public void setHasHeadsUpEntries(boolean hasHeadsUpEntries) { + mHasHeadsUpEntries = hasHeadsUpEntries; } public void setStackTopMargin(int stackTopMargin) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index aa9d3b23e47b..933a78009e29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5721,7 +5721,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable void setNumHeadsUp(long numHeadsUp) { mNumHeadsUp = numHeadsUp; - mAmbientState.setHasAlertEntries(numHeadsUp > 0); + mAmbientState.setHasHeadsUpEntries(numHeadsUp > 0); } public boolean getIsExpanded() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 830b8c1ae63e..78e6a795604a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -23,6 +23,7 @@ import static com.android.app.animation.Interpolators.STANDARD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING; import static com.android.server.notification.Flags.screenshareNotificationHiding; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.Flags.nsslFalsingFix; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener; @@ -71,7 +72,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.KeyguardMediaController; @@ -2078,7 +2078,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { } boolean horizontalSwipeWantsIt = false; boolean scrollerWantsIt = false; - if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled()) { + if (nsslFalsingFix() || migrateClocksToBlueprint()) { // Reverse the order relative to the else statement. onScrollTouch will reset on an // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes. if (mLongPressedView == null && !mView.isBeingDragged() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index a135802f095e..b0fefdd36012 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -27,6 +27,7 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import static com.android.systemui.Flags.lightRevealMigration; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.Flags.newAodTransition; import static com.android.systemui.Flags.predictiveBackSysui; import static com.android.systemui.Flags.truncatedStatusBarIconsFix; @@ -143,7 +144,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; @@ -1468,7 +1468,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return (v, event) -> { mAutoHideController.checkUserAutoHide(event); mRemoteInputManager.checkRemoteInputOutside(event); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mShadeController.onStatusBarTouch(event); } return getNotificationShadeWindowView().onTouchEvent(event); @@ -2505,7 +2505,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { mDeviceInteractive = true; - boolean isFlaggedOff = newAodTransition() && KeyguardShadeMigrationNssl.isEnabled(); + boolean isFlaggedOff = newAodTransition() && migrateClocksToBlueprint(); if (!isFlaggedOff && shouldAnimateDozeWakeup()) { // If this is false, the power button must be physically pressed in order to // trigger fingerprint authentication. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java index e79f3ff19031..94f62e075a4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.Flags.newAodTransition; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import android.content.Context; import android.content.res.Resources; @@ -40,7 +41,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -545,7 +545,7 @@ public class LegacyNotificationIconAreaControllerImpl implements return; } if (mScreenOffAnimationController.shouldAnimateAodIcons()) { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mAodIcons.setTranslationY(-mAodIconAppearTranslation); } mAodIcons.setAlpha(0); @@ -557,14 +557,14 @@ public class LegacyNotificationIconAreaControllerImpl implements .start(); } else { mAodIcons.setAlpha(1.0f); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mAodIcons.setTranslationY(0); } } } private void animateInAodIconTranslation() { - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mAodIcons.animate() .setInterpolator(Interpolators.DECELERATE_QUINT) .translationY(0) @@ -667,7 +667,7 @@ public class LegacyNotificationIconAreaControllerImpl implements } } else { mAodIcons.setAlpha(1.0f); - if (!KeyguardShadeMigrationNssl.isEnabled()) { + if (!migrateClocksToBlueprint()) { mAodIcons.setTranslationY(0); } mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 8ac3b4a75141..d10ca3d31de2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -38,9 +38,9 @@ import android.widget.LinearLayout; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dependency; import com.android.systemui.Gefingerpoken; -import com.android.systemui.res.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -67,6 +67,8 @@ public class PhoneStatusBarView extends FrameLayout { private int mStatusBarHeight; @Nullable private Gefingerpoken mTouchEventHandler; + private int mDensity; + private float mFontScale; /** * Draw this many pixels into the left/right side of the cutout to optimally use the space @@ -167,13 +169,23 @@ public class PhoneStatusBarView extends FrameLayout { mDisplayCutout = getRootWindowInsets().getDisplayCutout(); } - final Rect newSize = mContext.getResources().getConfiguration().windowConfiguration - .getMaxBounds(); + Configuration newConfiguration = mContext.getResources().getConfiguration(); + final Rect newSize = newConfiguration.windowConfiguration.getMaxBounds(); if (!Objects.equals(newSize, mDisplaySize)) { changed = true; mDisplaySize = newSize; } + int density = newConfiguration.densityDpi; + if (density != mDensity) { + changed = true; + mDensity = density; + } + float fontScale = newConfiguration.fontScale; + if (fontScale != mFontScale) { + changed = true; + mFontScale = fontScale; + } return changed; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index f9702dd12535..a39bfe00be28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -81,6 +81,7 @@ private constructor( statusContainer.setOnHoverListener( statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer) ) + statusContainer.setOnClickListener { shadeViewController.expand(/* animate= */true) } progressProvider?.setReadyToHandleTransition(true) configurationController.addCallback(configurationListener) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 665a5714e277..223eaf74e2e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -17,10 +17,10 @@ import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.systemui.DejankUtils import com.android.app.animation.Interpolators +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.shade.ShadeViewController import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealScrim @@ -286,7 +286,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( // up, with unpredictable consequences. if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY) && shouldAnimateInKeyguard) { - if (!KeyguardShadeMigrationNssl.isEnabled) { + if (!migrateClocksToBlueprint()) { // Tracking this state should no longer be relevant, as the isInteractive // check covers it aodUiAnimationPlaying = true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt index a7352be8d80a..420701f026d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt @@ -61,16 +61,16 @@ interface HeadsUpManager : Dumpable { fun getTouchableRegion(): Region? /** - * Whether or not there are any active alerting notifications. + * Whether or not there are any entries managed by HeadsUpManager. * - * @return true if there is an alert, false otherwise + * @return true if there is a heads up entry, false otherwise */ fun hasNotifications(): Boolean = false /** Returns whether there are any pinned Heads Up Notifications or not. */ fun hasPinnedHeadsUp(): Boolean - /** Returns whether or not the given notification is alerting and managed by this manager. */ + /** Returns whether or not the given notification is managed by this manager. */ fun isHeadsUpEntry(key: String): Boolean fun isHeadsUpGoingAway(): Boolean diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index eb08f37503c6..eba3162febe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -16,9 +16,12 @@ package com.android.systemui.statusbar.policy; +import android.annotation.FlaggedApi; import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.hardware.SensorPrivacyManager.Sources.Source; +import com.android.internal.camera.flags.Flags; + public interface IndividualSensorPrivacyController extends CallbackController<IndividualSensorPrivacyController.Callback> { void init(); @@ -42,6 +45,12 @@ public interface IndividualSensorPrivacyController extends */ boolean requiresAuthentication(); + /** + * @return whether camera privacy is enabled for the package. + */ + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + boolean isCameraPrivacyEnabled(String packageName); + interface Callback { void onSensorBlockedChanged(@Sensor int sensor, boolean blocked); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 87dfc9962675..58b82f166623 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; +import android.Manifest; +import android.annotation.FlaggedApi; +import android.annotation.RequiresPermission; import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.hardware.SensorPrivacyManager.Sources.Source; @@ -28,6 +31,8 @@ import android.util.SparseBooleanArray; import androidx.annotation.NonNull; +import com.android.internal.camera.flags.Flags; + import java.util.Set; public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { @@ -102,6 +107,13 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr } @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public boolean isCameraPrivacyEnabled(String packageName) { + return mSensorPrivacyManager.isCameraPrivacyEnabled(packageName); + } + + @Override public void addCallback(@NonNull Callback listener) { mCallbacks.add(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java index 2b0a92c6ecd7..6956a7d8a8e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java @@ -221,10 +221,15 @@ public class SensitiveNotificationProtectionControllerImpl // Exempt foreground service notifications from protection in effort to keep screen share // stop actions easily accessible StatusBarNotification sbn = entry.getSbn(); - if (sbn.getNotification().isFgsOrUij()) { - return !sbn.getPackageName().equals(projection.getPackageName()); + if (sbn.getNotification().isFgsOrUij() + && sbn.getPackageName().equals(projection.getPackageName())) { + return false; } - return true; + // Only protect/redact notifications if the developer has not explicitly set notification + // visibility as public and users has not adjusted default channel visibility to private + boolean notificationRequestsRedaction = entry.isNotificationVisibilityPrivate(); + boolean userForcesRedaction = entry.isChannelVisibilityPrivate(); + return notificationRequestsRedaction || userForcesRedaction; } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt index 668b1439abab..ca5ea3bc1caa 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt @@ -45,7 +45,6 @@ import com.android.wm.shell.displayareahelper.DisplayAreaHelper import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import java.lang.IllegalArgumentException import java.util.Optional import java.util.concurrent.Executor import java.util.function.Consumer @@ -71,7 +70,7 @@ constructor( private val displayTracker: DisplayTracker, @Background private val applicationScope: CoroutineScope, @Main private val executor: Executor, - @Assisted private val displaySelector: Sequence<DisplayInfo>.() -> DisplayInfo?, + @Assisted private val displaySelector: List<DisplayInfo>.() -> DisplayInfo?, @Assisted private val lightRevealEffectFactory: (rotation: Int) -> LightRevealEffect, @Assisted private val overlayContainerName: String ) { @@ -84,13 +83,11 @@ constructor( private var scrimView: LightRevealScrim? = null private val rotationWatcher = RotationWatcher() - private val internalDisplayInfos: Sequence<DisplayInfo> - get() = - displayManager - .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) - .asSequence() - .map { DisplayInfo().apply { it.getDisplayInfo(this) } } - .filter { it.type == Display.TYPE_INTERNAL } + private val internalDisplayInfos: List<DisplayInfo> = + displayManager + .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) + .map { DisplayInfo().apply { it.getDisplayInfo(this) } } + .filter { it.type == Display.TYPE_INTERNAL } var isTouchBlocked: Boolean = false set(value) { @@ -252,7 +249,7 @@ constructor( @AssistedFactory interface Factory { fun create( - displaySelector: Sequence<DisplayInfo>.() -> DisplayInfo?, + displaySelector: List<DisplayInfo>.() -> DisplayInfo?, effectFactory: (rotation: Int) -> LightRevealEffect, overlayContainerName: String ): FullscreenLightRevealAnimationController diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index 50d15475434b..e10d1cb833fa 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -60,6 +60,7 @@ public class Events { public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool) public static final int EVENT_ODI_CAPTIONS_CLICK = 21; public static final int EVENT_ODI_CAPTIONS_TOOLTIP_CLICK = 22; + public static final int EVENT_SLIDER_TOUCH_TRACKING = 23; // (tracking|bool) private static final String[] EVENT_TAGS = { "show_dialog", @@ -84,7 +85,8 @@ public class Events { "show_usb_overheat_alarm", "dismiss_usb_overheat_alarm", "odi_captions_click", - "odi_captions_tooltip_click" + "odi_captions_tooltip_click", + "slider_touch_tracking" }; public static final int DISMISS_REASON_UNKNOWN = 0; @@ -234,6 +236,10 @@ public class Events { VOLUME_DIALOG_SLIDER(150), @UiEvent(doc = "The audio stream was set to silent via slider") VOLUME_DIALOG_SLIDER_TO_ZERO(151), + @UiEvent(doc = "The right-most slider started tracking touch") + VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH(1620), + @UiEvent(doc = "The right-most slider stopped tracking touch") + VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH(1621), @UiEvent(doc = "ODI captions was clicked") VOLUME_DIALOG_ODI_CAPTIONS_CLICKED(1503), @UiEvent(doc = "ODI captions tooltip dismiss was clicked") @@ -491,6 +497,15 @@ public class Events { .append(" keyguard=").append(keyguard); } break; + case EVENT_SLIDER_TOUCH_TRACKING: + final boolean startedTracking = (boolean) list[0]; + final VolumeDialogEvent event; + if (startedTracking) { + event = VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH; + } else { + event = VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH; + } + sUiEventLogger.log(event); default: sb.append(Arrays.asList(list)); break; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 90c5c62718ad..404563087041 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -2518,6 +2518,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, @Override public void onStartTrackingTouch(SeekBar seekBar) { if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream); + Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */true); if (mRow.mHapticPlugin != null) { mRow.mHapticPlugin.onStartTrackingTouch(seekBar); } @@ -2528,6 +2529,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, @Override public void onStopTrackingTouch(SeekBar seekBar) { if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream); + Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */false); if (mRow.mHapticPlugin != null) { mRow.mHapticPlugin.onStopTrackingTouch(seekBar); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt index ab76d450eb0a..9f99e9778ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt @@ -24,6 +24,9 @@ import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory +import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl +import dagger.Binds import dagger.Module import dagger.Provides import kotlin.coroutines.CoroutineContext @@ -32,6 +35,11 @@ import kotlinx.coroutines.CoroutineScope @Module interface MediaDevicesModule { + @Binds + fun bindLocalMediaRepositoryFactory( + impl: LocalMediaRepositoryFactoryImpl + ): LocalMediaRepositoryFactory + companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt index 0a1ee249d6fb..1f52260bb20d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt @@ -26,7 +26,12 @@ import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -class LocalMediaRepositoryFactory +interface LocalMediaRepositoryFactory { + + fun create(packageName: String?): LocalMediaRepository +} + +class LocalMediaRepositoryFactoryImpl @Inject constructor( private val intentsReceiver: AudioManagerIntentsReceiver, @@ -34,9 +39,9 @@ constructor( private val localMediaManagerFactory: LocalMediaManagerFactory, @Application private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, -) { +) : LocalMediaRepositoryFactory { - fun create(packageName: String?): LocalMediaRepository = + override fun create(packageName: String?): LocalMediaRepository = LocalMediaRepositoryImpl( intentsReceiver, localMediaManagerFactory.create(packageName), diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt new file mode 100644 index 000000000000..020ec64c0491 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain + +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor +import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** Determines if the Media Output Volume Panel component is available. */ +@VolumePanelScope +class MediaOutputAvailabilityCriteria +@Inject +constructor( + private val mediaOutputInteractor: MediaOutputInteractor, + private val audioModeInteractor: AudioModeInteractor, +) : ComponentAvailabilityCriteria { + + override fun isAvailable(): Flow<Boolean> { + return combine(mediaOutputInteractor.mediaDevices, audioModeInteractor.isOngoingCall) { + devices, + isOngoingCall -> + !isOngoingCall && devices.isNotEmpty() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt new file mode 100644 index 000000000000..170b32c1d0ea --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.internal.jank.InteractionJankMonitor +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.animation.Expandable +import com.android.systemui.media.dialog.MediaOutputDialogFactory +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession +import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import javax.inject.Inject + +/** User actions interactor for Media Output Volume Panel component. */ +@VolumePanelScope +class MediaOutputActionsInteractor +@Inject +constructor( + private val mediaOutputDialogFactory: MediaOutputDialogFactory, + private val activityStarter: ActivityStarter, +) { + + fun onDeviceClick(expandable: Expandable) { + activityStarter.startActivity( + Intent(Settings.ACTION_BLUETOOTH_SETTINGS), + true, + expandable.activityTransitionController(), + ) + } + + fun onBarClick(session: MediaDeviceSession, expandable: Expandable) { + when (session) { + is MediaDeviceSession.Active -> { + mediaOutputDialogFactory.createWithController( + session.packageName, + false, + expandable.dialogController() + ) + } + is MediaDeviceSession.Inactive -> { + mediaOutputDialogFactory.createDialogForSystemRouting(expandable.dialogController()) + } + else -> { + /* do nothing */ + } + } + } + + private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? { + return dialogTransitionController( + cuj = + DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + MediaOutputDialogFactory.INTERACTION_JANK_TAG + ) + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt index 6c456f963f03..24cc29d8e1f9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt @@ -17,10 +17,14 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.content.pm.PackageManager +import android.media.session.MediaController +import android.os.Handler import android.util.Log import com.android.settingslib.media.MediaDevice import com.android.settingslib.volume.data.repository.LocalMediaRepository +import com.android.settingslib.volume.data.repository.MediaControllerChange import com.android.settingslib.volume.data.repository.MediaControllerRepository +import com.android.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession @@ -30,14 +34,21 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext +/** Provides observable models about the current media session state. */ @OptIn(ExperimentalCoroutinesApi::class) @VolumePanelScope class MediaOutputInteractor @@ -47,32 +58,44 @@ constructor( private val packageManager: PackageManager, @VolumePanelScope private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, + @Background private val backgroundHandler: Handler, mediaControllerRepository: MediaControllerRepository ) { - val mediaDeviceSession: Flow<MediaDeviceSession> = - mediaControllerRepository.activeMediaController.mapNotNull { mediaController -> - if (mediaController == null) { - MediaDeviceSession.Inactive - } else { + /** Current [MediaDeviceSession]. Emits when the session playback changes. */ + val mediaDeviceSession: StateFlow<MediaDeviceSession> = + mediaControllerRepository.activeLocalMediaController + .flatMapLatest { it?.mediaDeviceSession() ?: flowOf(MediaDeviceSession.Inactive) } + .flowOn(backgroundCoroutineContext) + .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSession.Inactive) + + private fun MediaController.mediaDeviceSession(): Flow<MediaDeviceSession> { + return stateChanges(backgroundHandler) + .onStart { emit(MediaControllerChange.PlaybackStateChanged(playbackState)) } + .filterIsInstance<MediaControllerChange.PlaybackStateChanged>() + .map { MediaDeviceSession.Active( - appLabel = getApplicationLabel(mediaController.packageName) - ?: return@mapNotNull null, - packageName = mediaController.packageName, - sessionToken = mediaController.sessionToken, + appLabel = getApplicationLabel(packageName) + ?: return@map MediaDeviceSession.Inactive, + packageName = packageName, + sessionToken = sessionToken, + playbackState = playbackState, ) } - } - private val localMediaRepository: Flow<LocalMediaRepository> = + } + + private val localMediaRepository: SharedFlow<LocalMediaRepository> = mediaDeviceSession .map { (it as? MediaDeviceSession.Active)?.packageName } .distinctUntilChanged() .map { localMediaRepositoryFactory.create(it) } - .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 1) + .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1) + /** Currently connected [MediaDevice]. */ val currentConnectedDevice: Flow<MediaDevice?> = localMediaRepository.flatMapLatest { it.currentConnectedDevice } + /** A list of available [MediaDevice]s. */ val mediaDevices: Flow<Collection<MediaDevice>> = localMediaRepository.flatMapLatest { it.mediaDevices } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt index f250308802b2..71df8e53b5e2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.model import android.media.session.MediaSession +import android.media.session.PlaybackState /** Represents media playing on the connected device. */ sealed interface MediaDeviceSession { @@ -26,6 +27,7 @@ sealed interface MediaDeviceSession { val appLabel: CharSequence, val packageName: String, val sessionToken: MediaSession.Token, + val playbackState: PlaybackState?, ) : MediaDeviceSession /** Media is not playing. */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt new file mode 100644 index 000000000000..8ba672d2a15e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel + +/** + * Models part of the Media Session Volume Panel component that displays connected device + * information. + */ +data class ConnectedDeviceViewModel( + val label: CharSequence, + val deviceName: CharSequence?, +) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt new file mode 100644 index 000000000000..e0718ace2c30 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel + +import com.android.systemui.common.shared.model.Color +import com.android.systemui.common.shared.model.Icon + +/** Models Media Session Volume Panel component connected device icon. */ +sealed interface DeviceIconViewModel { + + val icon: Icon + val backgroundColor: Color + + class IsPlaying( + override val icon: Icon, + override val backgroundColor: Color, + ) : DeviceIconViewModel + + class IsNotPlaying( + override val icon: Icon, + override val backgroundColor: Color, + ) : DeviceIconViewModel +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt new file mode 100644 index 000000000000..d14899294526 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel + +import android.content.Context +import com.android.systemui.animation.Expandable +import com.android.systemui.common.shared.model.Color +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.res.R +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession +import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope +import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn + +/** Models the UI of the Media Output Volume Panel component. */ +@VolumePanelScope +class MediaOutputViewModel +@Inject +constructor( + private val context: Context, + @VolumePanelScope private val coroutineScope: CoroutineScope, + private val volumePanelViewModel: VolumePanelViewModel, + private val actionsInteractor: MediaOutputActionsInteractor, + interactor: MediaOutputInteractor, +) { + + private val mediaDeviceSession: StateFlow<MediaDeviceSession> = + interactor.mediaDeviceSession.stateIn( + coroutineScope, + SharingStarted.Eagerly, + MediaDeviceSession.Unknown, + ) + + val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> = + combine(mediaDeviceSession, interactor.currentConnectedDevice) { + mediaDeviceSession, + currentConnectedDevice -> + ConnectedDeviceViewModel( + if (mediaDeviceSession.isPlaying()) { + context.getString( + R.string.media_output_label_title, + (mediaDeviceSession as MediaDeviceSession.Active).appLabel + ) + } else { + context.getString(R.string.media_output_title_without_playing) + }, + currentConnectedDevice?.name, + ) + } + .stateIn( + coroutineScope, + SharingStarted.Eagerly, + null, + ) + + val deviceIconViewModel: StateFlow<DeviceIconViewModel?> = + combine(mediaDeviceSession, interactor.currentConnectedDevice) { + mediaDeviceSession, + currentConnectedDevice -> + if (mediaDeviceSession.isPlaying()) { + val icon = + currentConnectedDevice?.icon?.let { Icon.Loaded(it, null) } + ?: Icon.Resource( + com.android.internal.R.drawable.ic_bt_headphones_a2dp, + null + ) + DeviceIconViewModel.IsPlaying( + icon, + Color.Attribute(com.android.internal.R.attr.materialColorSecondary), + ) + } else { + DeviceIconViewModel.IsNotPlaying( + Icon.Resource(R.drawable.ic_media_home_devices, null), + Color.Attribute(com.android.internal.R.attr.materialColorSurface), + ) + } + } + .stateIn( + coroutineScope, + SharingStarted.Eagerly, + null, + ) + + private fun MediaDeviceSession.isPlaying(): Boolean = + this is MediaDeviceSession.Active && playbackState?.isActive == true + + fun onDeviceClick(expandable: Expandable) { + actionsInteractor.onDeviceClick(expandable) + volumePanelViewModel.dismissPanel() + } + + fun onBarClick(expandable: Expandable) { + actionsInteractor.onBarClick(mediaDeviceSession.value, expandable) + volumePanelViewModel.dismissPanel() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt index 842c3234fe26..6c742ba7e5f9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt @@ -20,6 +20,7 @@ import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey object VolumePanelComponents { + const val MEDIA_OUTPUT: VolumePanelComponentKey = "media_output" const val BOTTOM_BAR: VolumePanelComponentKey = "bottom_bar" const val CAPTIONING: VolumePanelComponentKey = "captioning" } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt index 841daf85ebcc..afd3f6170d3d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.dagger import com.android.systemui.volume.panel.component.bottombar.BottomBarModule import com.android.systemui.volume.panel.component.captioning.CaptioningModule +import com.android.systemui.volume.panel.component.mediaoutput.MediaOutputModule import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.domain.DomainModule @@ -46,6 +47,7 @@ import kotlinx.coroutines.CoroutineScope // Components modules BottomBarModule::class, CaptioningModule::class, + MediaOutputModule::class, ] ) interface VolumePanelComponent { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt index defa92def893..55d8de5aeb95 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt @@ -51,6 +51,7 @@ interface DomainModule { fun provideEnabledComponents(): Collection<VolumePanelComponentKey> { return setOf( VolumePanelComponents.CAPTIONING, + VolumePanelComponents.MEDIA_OUTPUT, VolumePanelComponents.BOTTOM_BAR, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt index a3f052d88b7d..867df4a87dd5 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt @@ -37,7 +37,11 @@ interface UiModule { @Provides @VolumePanelScope @HeaderComponents - fun provideHeaderComponents(): Collection<VolumePanelComponentKey> = setOf() + fun provideHeaderComponents(): Collection<VolumePanelComponentKey> { + return setOf( + VolumePanelComponents.MEDIA_OUTPUT, + ) + } @Provides @VolumePanelScope diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt index 1b2265b9891e..53e1b8b5bb70 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt @@ -20,8 +20,8 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import androidx.core.view.WindowCompat import com.android.systemui.compose.ComposeFacade +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel import javax.inject.Inject @@ -32,6 +32,7 @@ class VolumePanelActivity constructor( private val volumePanelViewModelFactory: Provider<VolumePanelViewModel.Factory>, private val volumePanelFlag: VolumePanelFlag, + private val configurationController: ConfigurationController, ) : ComponentActivity() { private val viewModel: VolumePanelViewModel by @@ -43,7 +44,11 @@ constructor( volumePanelFlag.assertNewVolumePanel() - WindowCompat.setDecorFitsSystemWindows(window, false) ComposeFacade.setVolumePanelActivityContent(this, viewModel) { finish() } } + + override fun onContentChanged() { + super.onContentChanged() + configurationController.onConfigurationChanged(resources.configuration) + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt index f67db965e28a..7f33a6bb70f9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt @@ -22,10 +22,13 @@ import android.content.res.Configuration.Orientation /** * State of the Volume Panel itself. * - * @property orientation is current Volume Panel orientation. + * @property orientation is current Volume Panel orientation + * @property isWideScreen is true when Volume Panel should use wide-screen layout and false the + * otherwise */ data class VolumePanelState( @Orientation val orientation: Int, + val isWideScreen: Boolean, val isVisible: Boolean, ) { init { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt index d87a79ed90f4..3c5b75cfb349 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt @@ -21,6 +21,7 @@ import android.content.res.Resources import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.onConfigChanged import com.android.systemui.volume.panel.dagger.VolumePanelComponent @@ -72,7 +73,11 @@ class VolumePanelViewModel( .distinctUntilChanged(), mutablePanelVisibility, ) { configuration, isVisible -> - VolumePanelState(orientation = configuration.orientation, isVisible = isVisible) + VolumePanelState( + orientation = configuration.orientation, + isVisible = isVisible, + isWideScreen = !resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog), + ) } .stateIn( scope, @@ -80,6 +85,7 @@ class VolumePanelViewModel( VolumePanelState( orientation = resources.configuration.orientation, isVisible = mutablePanelVisibility.value, + isWideScreen = !resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog) ), ) val componentsLayout: Flow<ComponentsLayout> = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index cd19259091ab..30269664a559 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -260,7 +260,7 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun keyguardCallback_visibilityChanged_clockDozeCalled() = runBlocking(IMMEDIATE) { - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() verify(keyguardUpdateMonitor).registerCallback(capture(captor)) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index e8d86dddcda5..9d81b960336f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -35,6 +35,7 @@ import android.view.View; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.plugins.clocks.ClockFaceConfig; import com.android.systemui.plugins.clocks.ClockTickRate; import com.android.systemui.shared.clocks.ClockRegistry; @@ -50,6 +51,8 @@ import org.mockito.verification.VerificationMode; public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest { @Test public void testInit_viewAlreadyAttached() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + mController.init(); verifyAttachment(times(1)); @@ -57,6 +60,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testInit_viewNotYetAttached() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); @@ -73,12 +78,16 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testInitSubControllers() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + mController.init(); verify(mKeyguardSliceViewController).init(); } @Test public void testInit_viewDetached() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); mController.init(); @@ -92,6 +101,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testPluginPassesStatusBarState() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class); @@ -105,6 +116,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testSmartspaceEnabledRemovesKeyguardStatusArea() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSmartspaceController.isEnabled()).thenReturn(true); mController.init(); @@ -113,6 +126,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void onLocaleListChangedRebuildsSmartspaceView() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSmartspaceController.isEnabled()).thenReturn(true); mController.init(); @@ -123,6 +138,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSmartspaceController.isEnabled()).thenReturn(true); when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true); mController.init(); @@ -136,6 +153,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testSmartspaceDisabledShowsKeyguardStatusArea() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSmartspaceController.isEnabled()).thenReturn(false); mController.init(); @@ -144,6 +163,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testRefresh() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + mController.refresh(); verify(mSmartspaceController).requestSmartspaceUpdate(); @@ -151,6 +172,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testChangeToDoubleLineClockSetsSmallClock() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSecureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1, UserHandle.USER_CURRENT)) .thenReturn(0); @@ -174,11 +197,15 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testGetClock_ForwardsToClock() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + assertEquals(mClockController, mController.getClock()); } @Test public void testGetLargeClockBottom_returnsExpectedValue() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mLargeClockFrame.getVisibility()).thenReturn(View.VISIBLE); when(mLargeClockFrame.getHeight()).thenReturn(100); when(mSmallClockFrame.getHeight()).thenReturn(50); @@ -191,6 +218,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testGetSmallLargeClockBottom_returnsExpectedValue() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mLargeClockFrame.getVisibility()).thenReturn(View.GONE); when(mLargeClockFrame.getHeight()).thenReturn(100); when(mSmallClockFrame.getHeight()).thenReturn(50); @@ -203,12 +232,16 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testGetClockBottom_nullClock_returnsZero() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mClockEventController.getClock()).thenReturn(null); assertEquals(0, mController.getClockBottom(10)); } @Test public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mSmartspaceController.isWeatherEnabled()).thenReturn(true); ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(ContentObserver.class); @@ -227,6 +260,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class); when(mSmartspaceController.isEnabled()).thenReturn(true); @@ -249,11 +284,15 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testGetClock_nullClock_returnsNull() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + when(mClockEventController.getClock()).thenReturn(null); assertNull(mController.getClock()); } private void verifyAttachment(VerificationMode times) { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + verify(mClockRegistry, times).registerClockChangeListener( any(ClockRegistry.ClockChangeListener.class)); verify(mClockEventController, times).registerListeners(mView); @@ -261,6 +300,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testSplitShadeEnabledSetToSmartspaceController() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + mController.setSplitShadeEnabled(true); verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true); verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false); @@ -268,6 +309,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro @Test public void testSplitShadeDisabledSetToSmartspaceController() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + mController.setSplitShadeEnabled(false); verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false); verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 4508aea81176..b4a9d40a6caf 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -40,6 +40,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.clocks.ClockController; import com.android.systemui.plugins.clocks.ClockFaceController; @@ -79,6 +80,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { @Before public void setUp() { + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); + MockitoAnnotations.initMocks(this); when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index be06cc5d3d1d..538daee52377 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -1500,7 +1500,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); - mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true); mTestableLooper.processAllMessages(); @@ -2016,6 +2015,34 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void authenticateFingerprint_onFaceLockout_detectFingerprint() throws RemoteException { + // GIVEN fingerprintAuthenticate + mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); + mTestableLooper.processAllMessages(); + verifyFingerprintAuthenticateCall(); + verifyFingerprintDetectNeverCalled(); + clearInvocations(mFingerprintManager); + + // WHEN class 3 face is locked out + when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true); + when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true); + setupFingerprintAuth(/* isClass3 */ true); + // GIVEN primary auth is not required by StrongAuthTracker + primaryAuthNotRequiredByStrongAuthTracker(); + + // WHEN face (class 3) is locked out + faceAuthLockOut(); + mTestableLooper.processAllMessages(); + + // THEN unlocking with fingerprint is not allowed + Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FINGERPRINT)); + + // THEN fingerprint detect gets called + verifyFingerprintDetectCall(); + } + + @Test public void testFingerprintSensorProperties() throws RemoteException { mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered( new ArrayList<>()); 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 43f7c60721ee..2c1a87d86be9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -20,6 +20,8 @@ import android.content.pm.PackageManager import android.hardware.biometrics.BiometricAuthenticator import android.hardware.biometrics.BiometricConstants import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT +import android.hardware.biometrics.Flags.customBiometricPrompt import android.hardware.biometrics.PromptInfo import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorPropertiesInternal @@ -38,11 +40,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R +import com.android.systemui.Flags.FLAG_CONSTRAINT_BP import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository -import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor @@ -53,6 +55,7 @@ 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.res.R import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.events.ANIMATING_OUT import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -145,6 +148,8 @@ open class AuthContainerViewTest : SysuiTestCase() { @Before fun setup() { + mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT) + mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP) displayRepository = FakeDisplayRepository() displayStateInteractor = @@ -394,6 +399,19 @@ open class AuthContainerViewTest : SysuiTestCase() { } @Test + fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() { + mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT) + val container = initializeFingerprintContainer( + authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL + ) + waitForIdleSync() + + assertThat(customBiometricPrompt()).isTrue() + assertThat(container.hasBiometricPrompt()).isTrue() + assertThat(container.hasCredentialView()).isFalse() + } + + @Test fun testCredentialViewUsesEffectiveUserId() { whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200) whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt index 936aa8bb819c..91da88e480d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.flags import android.testing.AndroidTestingRunner +import android.util.Log import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import java.io.PrintWriter @@ -41,14 +42,21 @@ class FlagDependenciesTest : SysuiTestCase() { FlagDependencies(TestFeatureFlags(teamfood = teamfood), TestHandler()) private class TestHandler : FlagDependenciesBase.Handler { + override val enableDependencies: Boolean + get() = true override fun warnAboutBadFlagConfiguration( all: List<FlagDependenciesBase.Dependency>, unmet: List<FlagDependenciesBase.Dependency> ) { - val title = "${unmet.size} invalid of ${all.size} flag dependencies" + val title = "Invalid flag dependencies: ${unmet.size}" val details = unmet.joinToString("\n") fail("$title:\n$details") } + + override fun onCollected(all: List<FlagDependenciesBase.Dependency>) { + Log.d("FlagDependencies", "All: ${all.size}") + all.forEach { Log.d("FlagDependencies", " $it") } + } } private class TestFeatureFlags(val teamfood: Boolean) : FeatureFlagsClassic { 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 2732047b4eba..0957748c9938 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; 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; @@ -650,6 +651,25 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } @Test + public void testBouncerPrompt_deviceLockedByAdaptiveAuth() { + // GIVEN no trust agents enabled and biometrics aren't enrolled + when(mUpdateMonitor.isTrustUsuallyManaged(anyInt())).thenReturn(false); + when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(false); + + // WHEN the strong auth reason is SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST + KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker = + mock(KeyguardUpdateMonitor.StrongAuthTracker.class); + when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(strongAuthTracker); + when(strongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); + when(strongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn( + SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST); + + // THEN the bouncer prompt reason should return PROMPT_REASON_ADAPTIVE_AUTH_REQUEST + assertEquals(KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST, + mViewMediator.mViewMediatorCallback.getBouncerPromptReason()); + } + + @Test public void testBouncerPrompt_deviceRestartedDueToMainlineUpdate() { // GIVEN biometrics enrolled when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index ad86ee9f07d2..9c7f254a7b85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection +import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import com.android.systemui.util.mockito.whenever @@ -71,6 +72,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { @Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection @Mock private lateinit var clockSection: ClockSection @Mock private lateinit var smartspaceSection: SmartspaceSection + @Mock private lateinit var keyguardSliceViewSection: KeyguardSliceViewSection @Mock private lateinit var udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection @Before @@ -92,6 +94,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { communalTutorialIndicatorSection, clockSection, smartspaceSection, + keyguardSliceViewSection, udfpsAccessibilityOverlaySection, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt index deb3a83fcbee..8eccde7d6cb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt @@ -88,6 +88,8 @@ class SmartspaceSectionTest : SysuiTestCase() { whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay) .thenReturn(hasCustomWeatherDataDisplay) whenever(keyguardClockViewModel.clockShouldBeCentered).thenReturn(clockShouldBeCentered) + whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true) + constraintSet = ConstraintSet() } @@ -103,7 +105,6 @@ class SmartspaceSectionTest : SysuiTestCase() { @Test fun testAddViews_smartspaceEnabled_dateWeatherDecoupled() { - whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true) whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true) underTest.addViews(constraintLayout) assert(smartspaceView.parent == constraintLayout) @@ -113,7 +114,6 @@ class SmartspaceSectionTest : SysuiTestCase() { @Test fun testAddViews_smartspaceEnabled_notDateWeatherDecoupled() { - whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true) whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(false) underTest.addViews(constraintLayout) assert(smartspaceView.parent == constraintLayout) @@ -123,7 +123,6 @@ class SmartspaceSectionTest : SysuiTestCase() { @Test fun testConstraintsWhenNotHasCustomWeatherDataDisplay() { - whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true) whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true) hasCustomWeatherDataDisplay.value = false underTest.addViews(constraintLayout) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index 59965022d7cc..c896486339b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -40,6 +40,7 @@ import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle +import android.platform.test.annotations.EnableFlags import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.AndroidTestingRunner @@ -61,6 +62,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.internal.widget.CachingIconView import com.android.systemui.ActivityIntentHelper +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.broadcast.BroadcastSender @@ -88,6 +90,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView import com.android.systemui.surfaceeffects.ripple.MultiRippleView import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView @@ -190,6 +193,7 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var dismissText: TextView private lateinit var multiRippleView: MultiRippleView private lateinit var turbulenceNoiseView: TurbulenceNoiseView + private lateinit var loadingEffectView: LoadingEffectView private lateinit var session: MediaSession private lateinit var device: MediaDeviceData @@ -402,6 +406,7 @@ public class MediaControlPanelTest : SysuiTestCase() { multiRippleView = MultiRippleView(context, null) turbulenceNoiseView = TurbulenceNoiseView(context, null) + loadingEffectView = LoadingEffectView(context, null) whenever(viewHolder.player).thenReturn(view) whenever(viewHolder.appIcon).thenReturn(appIcon) @@ -447,6 +452,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.multiRippleView).thenReturn(multiRippleView) whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView) + whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView) } /** Initialize elements for the recommendation view holder */ @@ -2429,6 +2435,7 @@ public class MediaControlPanelTest : SysuiTestCase() { mainExecutor.execute { assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) clock.advanceTime( MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION + @@ -2436,6 +2443,40 @@ public class MediaControlPanelTest : SysuiTestCase() { ) assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + } + } + + @Test + @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR) + fun playTurbulenceNoise_newLoadingEffect_finishesAfterDuration() { + val semanticActions = + MediaButton( + playOrPause = + MediaAction( + icon = null, + action = {}, + contentDescription = "play", + background = null + ) + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.actionPlayPause.callOnClick() + + mainExecutor.execute { + assertThat(loadingEffectView.visibility).isEqualTo(View.VISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + + clock.advanceTime( + MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION + + TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS.toLong() + ) + + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) } } @@ -2458,6 +2499,30 @@ public class MediaControlPanelTest : SysuiTestCase() { viewHolder.action0.callOnClick() assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + } + + @Test + @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR) + fun playTurbulenceNoise_newLoadingEffect_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() { + val semanticActions = + MediaButton( + custom0 = + MediaAction( + icon = null, + action = {}, + contentDescription = "custom0", + background = null + ), + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.action0.callOnClick() + + assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE) + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java index bfb18c88bf9b..52859cdeb406 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java @@ -22,6 +22,8 @@ import static android.view.Display.INVALID_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; @@ -289,32 +291,43 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class)); } - @Test public void testConfigurationChange_taskbarNotInitialized() { Configuration configuration = mContext.getResources().getConfiguration(); - when(Utilities.isLargeScreen(any())).thenReturn(true); + mNavigationBarController.mIsLargeScreen = true; mNavigationBarController.onConfigChanged(configuration); verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration); } @Test - public void testConfigurationChangeUnfolding_taskbarInitialized() { + public void testConfigurationChange_taskbarInitialized() { Configuration configuration = mContext.getResources().getConfiguration(); - when(Utilities.isLargeScreen(any())).thenReturn(true); + mNavigationBarController.mIsLargeScreen = true; when(mTaskbarDelegate.isInitialized()).thenReturn(true); mNavigationBarController.onConfigChanged(configuration); verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration); } @Test - public void testConfigurationChangeFolding_taskbarInitialized() { + public void testShouldRenderTaskbar_taskbarNotRenderedOnPhone() { + mNavigationBarController.mIsLargeScreen = false; + mNavigationBarController.mIsPhone = true; + assertFalse(mNavigationBarController.supportsTaskbar()); + } + + @Test + public void testShouldRenderTaskbar_taskbarRenderedOnTabletOrUnfolded() { + mNavigationBarController.mIsLargeScreen = true; + mNavigationBarController.mIsPhone = false; + assertTrue(mNavigationBarController.supportsTaskbar()); + } + + @Test + public void testShouldRenderTaskbar_taskbarRenderedInFoldedState() { assumeTrue(enableTaskbarNavbarUnification()); - Configuration configuration = mContext.getResources().getConfiguration(); - when(Utilities.isLargeScreen(any())).thenReturn(false); - when(mTaskbarDelegate.isInitialized()).thenReturn(true); - mNavigationBarController.onConfigChanged(configuration); - verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration); + mNavigationBarController.mIsLargeScreen = false; + mNavigationBarController.mIsPhone = false; + assertTrue(mNavigationBarController.supportsTaskbar()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index cc27cbd9d809..44b897447759 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -103,7 +103,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; @@ -388,7 +387,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false); mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false); - mSetFlagsRule.disableFlags(KeyguardShadeMigrationNssl.FLAG_NAME); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 2e8d46a83e1c..059053c58e39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -57,7 +57,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.DejankUtils; import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.res.R; @@ -363,7 +362,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test public void onInterceptTouchEvent_nsslMigrationOff_userActivity() { - mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL); + mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, @@ -374,7 +373,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test public void onInterceptTouchEvent_nsslMigrationOn_userActivity_not_called() { - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL); + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, @@ -1125,7 +1124,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test public void nsslFlagEnabled_allowOnlyExternalTouches() { - mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME); + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); // This sets the dozing state that is read when onMiddleClicked is eventually invoked. mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 248ed249c213..c2267903440a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -44,7 +44,6 @@ import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_FEATURES import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.res.R @@ -378,7 +377,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Test fun handleDispatchTouchEvent_nsslMigrationOff_userActivity_not_called() { - mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL) + mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) underTest.setStatusBarViewController(phoneStatusBarViewController) interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -388,7 +387,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Test fun handleDispatchTouchEvent_nsslMigrationOn_userActivity() { - mSetFlagsRule.enableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL) + mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) underTest.setStatusBarViewController(phoneStatusBarViewController) interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -430,7 +429,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { // AND the lock icon wants the touch whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true) - mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME) + mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) // THEN touch should NOT be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() @@ -449,7 +448,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) .thenReturn(false) - mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME) + mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) // THEN touch should NOT be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -468,7 +467,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) .thenReturn(true) - mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME) + mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) // THEN touch should NOT be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt index c32635020ddc..d7eada82b9a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.fragments.FragmentService -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener import com.android.systemui.plugins.qs.QS @@ -100,7 +99,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) fakeSystemClock = FakeSystemClock() delayableExecutor = FakeExecutor(fakeSystemClock) - mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME) + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) mContext.ensureTestableResources() whenever(view.context).thenReturn(mContext) whenever(view.resources).thenReturn(mContext.resources) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 64fd80d72d3f..74d017375eb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq +import com.android.systemui.util.ThreadAssert import java.util.function.BiConsumer import junit.framework.Assert.assertEquals import junit.framework.Assert.fail @@ -69,6 +70,7 @@ class ClockRegistryTest : SysuiTestCase() { @Mock private lateinit var mockDefaultClock: ClockController @Mock private lateinit var mockThumbnail: Drawable @Mock private lateinit var mockContentResolver: ContentResolver + @Mock private lateinit var mockThreadAssert: ThreadAssert private lateinit var fakeDefaultProvider: FakeClockPlugin private lateinit var pluginListener: PluginListener<ClockProviderPlugin> private lateinit var registry: ClockRegistry @@ -163,14 +165,12 @@ class ClockRegistryTest : SysuiTestCase() { defaultClockProvider = fakeDefaultProvider, keepAllLoaded = false, subTag = "Test", + assert = mockThreadAssert, ) { override fun querySettings() { } override fun applySettings(value: ClockSettings?) { settings = value } - // Unit Test does not validate threading - override fun assertMainThread() {} - override fun assertNotMainThread() {} } registry.registerListeners() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java index ccc9dc0d9618..8a48fe10d7fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java @@ -50,8 +50,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.res.R; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.util.time.FakeSystemClock; @@ -280,6 +280,66 @@ public class NotificationEntryTest extends SysuiTestCase { } @Test + public void testIsNotificationVisibilityPrivate_true() { + assertTrue(mEntry.isNotificationVisibilityPrivate()); + } + + @Test + public void testIsNotificationVisibilityPrivate_visibilityPublic_false() { + Notification.Builder notification = new Notification.Builder(mContext, "") + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text"); + + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setUid(TEST_UID) + .setChannel(mChannel) + .setId(mId++) + .setNotification(notification.build()) + .setUser(new UserHandle(ActivityManager.getCurrentUser())) + .build(); + + assertFalse(entry.isNotificationVisibilityPrivate()); + } + + @Test + public void testIsChannelVisibilityPrivate_true() { + assertTrue(mEntry.isChannelVisibilityPrivate()); + } + + @Test + public void testIsChannelVisibilityPrivate_visibilityPublic_false() { + NotificationChannel channel = + new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); + channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + StatusBarNotification sbn = new SbnBuilder().build(); + Ranking ranking = new RankingBuilder() + .setChannel(channel) + .setKey(sbn.getKey()) + .build(); + NotificationEntry entry = + new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); + + assertFalse(entry.isChannelVisibilityPrivate()); + } + + @Test + public void testIsChannelVisibilityPrivate_entryHasNoChannel_false() { + StatusBarNotification sbn = new SbnBuilder().build(); + Ranking ranking = new RankingBuilder() + .setChannel(null) + .setKey(sbn.getKey()) + .build(); + NotificationEntry entry = + new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); + + assertFalse(entry.isChannelVisibilityPrivate()); + } + + @Test public void notificationDataEntry_testIsLastMessageFromReply() { Person.Builder person = new Person.Builder() .setName("name") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt index 3f3de009fb04..fa669fc222f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt @@ -105,7 +105,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { @Test fun testSetLastAudiblyAlerted() { afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) - verify(controller1).setLastAudiblyAlertedMs(eq(17.toLong())) + verify(controller1).setLastAudibleMs(eq(17.toLong())) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index b7560ad79026..1687ccbf5826 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -22,15 +22,16 @@ import android.app.StatusBarManager.WINDOW_STATE_SHOWING import android.app.StatusBarManager.WINDOW_STATUS_BAR import android.view.LayoutInflater import android.view.MotionEvent +import android.view.View import android.view.ViewTreeObserver import android.view.ViewTreeObserver.OnPreDrawListener import android.widget.FrameLayout import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeControllerImpl @@ -48,8 +49,6 @@ import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat -import java.util.Optional -import javax.inject.Provider import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor @@ -60,6 +59,8 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import java.util.Optional +import javax.inject.Provider @SmallTest class PhoneStatusBarViewControllerTest : SysuiTestCase() { @@ -98,7 +99,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { val parent = FrameLayout(mContext) // add parent to keep layout params view = LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false) - as PhoneStatusBarView + as PhoneStatusBarView controller = createAndInitController(view) } } @@ -231,6 +232,27 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { verify(centralSurfacesImpl).setInteracting(any(), any()) } + @Test + fun shadeIsExpandedOnStatusIconClick() { + val view = createViewMock() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + val statusContainer = view.requireViewById<View>(R.id.system_icons) + statusContainer.performClick() + verify(shadeViewController).expand(any()) + } + + @Test + fun shadeIsNotExpandedOnStatusBarGeneralClick() { + val view = createViewMock() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + view.performClick() + verify(shadeViewController, never()).expand(any()) + } + private fun getCommandQueueCallback(): CommandQueue.Callbacks { val captor = argumentCaptor<CommandQueue.Callbacks>() verify(commandQueue).addCallback(captor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt index 269b70fe6dfb..5e8b62e799c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt @@ -207,6 +207,72 @@ class PhoneStatusBarViewTest : SysuiTestCase() { } @Test + fun onConfigurationChanged_noRelevantChange_doesNotUpdateInsets() { + val previousInsets = + Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10) + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(previousInsets) + context.orCreateTestableResources.overrideConfiguration(Configuration()) + view.onAttachedToWindow() + + val newInsets = Insets.NONE + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(newInsets) + view.onConfigurationChanged(Configuration()) + + assertThat(view.paddingLeft).isEqualTo(previousInsets.left) + assertThat(view.paddingTop).isEqualTo(previousInsets.top) + assertThat(view.paddingRight).isEqualTo(previousInsets.right) + assertThat(view.paddingBottom).isEqualTo(0) + } + + @Test + fun onConfigurationChanged_densityChanged_updatesInsets() { + val previousInsets = + Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10) + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(previousInsets) + val configuration = Configuration() + configuration.densityDpi = 123 + context.orCreateTestableResources.overrideConfiguration(configuration) + view.onAttachedToWindow() + + val newInsets = Insets.NONE + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(newInsets) + configuration.densityDpi = 456 + view.onConfigurationChanged(configuration) + + assertThat(view.paddingLeft).isEqualTo(newInsets.left) + assertThat(view.paddingTop).isEqualTo(newInsets.top) + assertThat(view.paddingRight).isEqualTo(newInsets.right) + assertThat(view.paddingBottom).isEqualTo(0) + } + + @Test + fun onConfigurationChanged_fontScaleChanged_updatesInsets() { + val previousInsets = + Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10) + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(previousInsets) + val configuration = Configuration() + configuration.fontScale = 1f + context.orCreateTestableResources.overrideConfiguration(configuration) + view.onAttachedToWindow() + + val newInsets = Insets.NONE + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(newInsets) + configuration.fontScale = 2f + view.onConfigurationChanged(configuration) + + assertThat(view.paddingLeft).isEqualTo(newInsets.left) + assertThat(view.paddingTop).isEqualTo(newInsets.top) + assertThat(view.paddingRight).isEqualTo(newInsets.right) + assertThat(view.paddingBottom).isEqualTo(0) + } + + @Test fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() { val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50) whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt index 1dac642836c6..a2af38f77f41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt @@ -19,17 +19,24 @@ package com.android.systemui.statusbar.policy import android.app.ActivityOptions import android.app.IActivityManager import android.app.Notification +import android.app.Notification.FLAG_FOREGROUND_SERVICE +import android.app.Notification.VISIBILITY_PRIVATE +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationChannel +import android.app.NotificationManager.IMPORTANCE_HIGH +import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS -import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.server.notification.Flags import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.mockito.whenever @@ -316,6 +323,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.shouldProtectNotification(notificationEntry)) } + @Test + fun shouldProtectNotification_projectionActive_publicNotification_false() { + mediaProjectionCallback.onStart(mediaProjectionInfo) + + // App marked notification visibility as public + val notificationEntry = setupPublicNotificationEntry(TEST_PROJECTION_PACKAGE_NAME) + + assertFalse(controller.shouldProtectNotification(notificationEntry)) + } + + @Test + fun shouldProtectNotification_projectionActive_publicNotificationUserChannelOverride_true() { + mediaProjectionCallback.onStart(mediaProjectionInfo) + + val notificationEntry = + setupPublicNotificationEntryWithUserOverriddenChannel(TEST_PROJECTION_PACKAGE_NAME) + + assertTrue(controller.shouldProtectNotification(notificationEntry)) + } private fun setDisabledViaDeveloperOption() { globalSettings.putInt(DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1) @@ -336,21 +362,50 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private fun setupNotificationEntry( packageName: String, - isFgs: Boolean = false + isFgs: Boolean = false, + overrideVisibility: Boolean = false, + overrideChannelVisibility: Boolean = false, ): NotificationEntry { - val notificationEntry = mock(NotificationEntry::class.java) - val sbn = mock(StatusBarNotification::class.java) - val notification = mock(Notification::class.java) - whenever(notificationEntry.sbn).thenReturn(sbn) - whenever(sbn.packageName).thenReturn(packageName) - whenever(sbn.notification).thenReturn(notification) - whenever(notification.isFgsOrUij).thenReturn(isFgs) - + val notification = Notification() + if (isFgs) { + notification.flags = notification.flags or FLAG_FOREGROUND_SERVICE + } + if (overrideVisibility) { + // Developer has marked notification as public + notification.visibility = VISIBILITY_PUBLIC + } + val notificationEntry = + NotificationEntryBuilder().setNotification(notification).setPkg(packageName).build() + val channel = NotificationChannel("1", "1", IMPORTANCE_HIGH) + if (overrideChannelVisibility) { + // User doesn't allow private notifications at the channel level + channel.lockscreenVisibility = VISIBILITY_PRIVATE + } + notificationEntry.setRanking( + RankingBuilder(notificationEntry.ranking) + .setChannel(channel) + .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) + .build() + ) return notificationEntry } private fun setupFgsNotificationEntry(packageName: String): NotificationEntry { - return setupNotificationEntry(packageName, /* isFgs= */ true) + return setupNotificationEntry(packageName, isFgs = true) + } + + private fun setupPublicNotificationEntry(packageName: String): NotificationEntry { + return setupNotificationEntry(packageName, overrideVisibility = true) + } + + private fun setupPublicNotificationEntryWithUserOverriddenChannel( + packageName: String + ): NotificationEntry { + return setupNotificationEntry( + packageName, + overrideVisibility = true, + overrideChannelVisibility = true + ) } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 25a0bc4fba61..1a3cb87b3422 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -58,6 +58,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import android.widget.ImageButton; +import android.widget.SeekBar; import androidx.test.core.view.MotionEventBuilder; import androidx.test.filters.SmallTest; @@ -111,6 +112,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { View mDrawerVibrate; View mDrawerMute; View mDrawerNormal; + ViewGroup mDialogRowsView; CaptionsToggleImageButton mODICaptionsIcon; private TestableLooper mTestableLooper; @@ -222,6 +224,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { } mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon); + mDialogRowsView = mDialog.getDialogView().findViewById(R.id.volume_dialog_rows); + Prefs.putInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1); @@ -671,6 +675,45 @@ public class VolumeDialogImplTest extends SysuiTestCase { } @Test + public void volumeSliderTracksTouch_logsStartAndStopTrackingUiEvents() { + UiEventLoggerFake logger = new UiEventLoggerFake(); + Events.sUiEventLogger = logger; + + mDialog.show(SHOW_REASON_UNKNOWN); + mTestableLooper.processAllMessages(); + + MotionEvent down = MotionEventBuilder.newBuilder() + .setAction(MotionEvent.ACTION_DOWN).build(); + MotionEvent up = MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_UP).build(); + + SeekBar slider = + mDialogRowsView.getChildAt(0).findViewById(R.id.volume_row_slider); + slider.onTouchEvent(down); + slider.onTouchEvent(up); + mTestableLooper.moveTimeForward(300); + mTestableLooper.processAllMessages(); + + boolean foundStartTrackingTouch = false; + boolean foundStopTrackingTouch = false; + for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) { + if (event.eventId + == Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH.getId() + ) { + foundStartTrackingTouch = true; + } + if (event.eventId + == Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH.getId() + ) { + foundStopTrackingTouch = true; + } + } + Assert.assertTrue("Did not log the event of start tracking touch.", + foundStartTrackingTouch); + Assert.assertTrue("Did not log the event of stop tracking touch.", + foundStopTrackingTouch); + } + + @Test public void turnOnDnD_volumeSliderIconChangesToDnd() { State state = createShellState(); state.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index b62b3a211e58..353d9709b056 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -46,6 +46,7 @@ import androidx.core.animation.AndroidXAnimatorIsolationRule; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; +import com.android.internal.protolog.common.ProtoLog; import com.android.systemui.broadcast.FakeBroadcastDispatcher; import com.android.systemui.flags.SceneContainerRule; @@ -170,6 +171,7 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { + ProtoLog.REQUIRE_PROTOLOGTOOL = false; mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver()); mDependency = mSysuiDependency.install(); // TODO(b/292141694): build out Ravenwood support for Instrumentation diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt index 97f84c67f473..43897c985c2c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt @@ -18,7 +18,7 @@ package com.android.systemui.flags import android.platform.test.annotations.EnableFlags import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR -import com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL +import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER import com.android.systemui.Flags.FLAG_SCENE_CONTAINER @@ -29,7 +29,7 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER @EnableFlags( FLAG_SCENE_CONTAINER, FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, - FLAG_KEYGUARD_SHADE_MIGRATION_NSSL, + FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_MEDIA_IN_SCENE_CONTAINER, ) @Retention(AnnotationRetention.RUNTIME) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt new file mode 100644 index 000000000000..e1b1966aed6c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.media.dialog.MediaOutputDialogFactory +import com.android.systemui.util.mockito.mock + +var Kosmos.mediaOutputDialogFactory: MediaOutputDialogFactory by Kosmos.Fixture { mock {} } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt new file mode 100644 index 000000000000..3f20df3376d9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume + +import android.content.packageManager +import android.content.pm.ApplicationInfo +import android.media.session.MediaController +import android.os.Handler +import android.testing.TestableLooper +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.media.mediaOutputDialogFactory +import com.android.systemui.plugins.activityStarter +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.volume.data.repository.FakeLocalMediaRepository +import com.android.systemui.volume.data.repository.FakeMediaControllerRepository +import com.android.systemui.volume.panel.component.mediaoutput.data.repository.FakeLocalMediaRepositoryFactory +import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor + +var Kosmos.mediaController: MediaController by Kosmos.Fixture { mock {} } + +val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() } +val Kosmos.localMediaRepositoryFactory: LocalMediaRepositoryFactory by + Kosmos.Fixture { FakeLocalMediaRepositoryFactory { localMediaRepository } } + +val Kosmos.mediaOutputActionsInteractor by + Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) } +val Kosmos.mediaControllerRepository by Kosmos.Fixture { FakeMediaControllerRepository() } +val Kosmos.mediaOutputInteractor by + Kosmos.Fixture { + MediaOutputInteractor( + localMediaRepositoryFactory, + packageManager.apply { + val appInfo: ApplicationInfo = mock { + whenever(loadLabel(any())).thenReturn("test_label") + } + whenever(getApplicationInfo(any(), any<Int>())).thenReturn(appInfo) + }, + testScope.backgroundScope, + testScope.testScheduler, + Handler(TestableLooper.get(testCase).looper), + mediaControllerRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt new file mode 100644 index 000000000000..5e1f85c70a1b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume + +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.volume.data.repository.FakeAudioRepository + +val Kosmos.audioRepository by Kosmos.Fixture { FakeAudioRepository() } +val Kosmos.audioModeInteractor by Kosmos.Fixture { AudioModeInteractor(audioRepository) } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt index dddf8e82d5f7..fed3e171862d 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.android.settingslib.volume.data.repository +package com.android.systemui.volume.data.repository import android.media.AudioDeviceInfo +import com.android.settingslib.volume.data.repository.AudioRepository import com.android.settingslib.volume.shared.model.AudioStream import com.android.settingslib.volume.shared.model.AudioStreamModel import com.android.settingslib.volume.shared.model.RingerMode diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt index 642b72c70e55..7835fc89ea52 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt @@ -1,23 +1,24 @@ /* * Copyright (C) 2024 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package com.android.settingslib.volume.data.repository +package com.android.systemui.volume.data.repository import com.android.settingslib.media.MediaDevice import com.android.settingslib.volume.data.model.RoutingSession +import com.android.settingslib.volume.data.repository.LocalMediaRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt new file mode 100644 index 000000000000..6d52e525d238 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.data.repository + +import android.media.session.MediaController +import com.android.settingslib.volume.data.repository.MediaControllerRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeMediaControllerRepository : MediaControllerRepository { + + private val mutableActiveLocalMediaController = MutableStateFlow<MediaController?>(null) + override val activeLocalMediaController: StateFlow<MediaController?> = + mutableActiveLocalMediaController.asStateFlow() + + fun setActiveLocalMediaController(controller: MediaController?) { + mutableActiveLocalMediaController.value = controller + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt new file mode 100644 index 000000000000..ad8ccb00f45f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.media.mediaOutputDialogFactory +import com.android.systemui.plugins.activityStarter +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor + +val Kosmos.mediaOutputActionsInteractor by + Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt new file mode 100644 index 000000000000..1b3480c423e4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.data.repository + +import com.android.settingslib.volume.data.repository.LocalMediaRepository + +class FakeLocalMediaRepositoryFactory( + val provider: (packageName: String?) -> LocalMediaRepository +) : LocalMediaRepositoryFactory { + + override fun create(packageName: String?): LocalMediaRepository = provider(packageName) +} diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index c134a4c5b2ad..d8a94d8b8b59 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -1204,23 +1204,26 @@ public class CameraExtensionsProxyService extends Service { List<Pair<CameraCharacteristics.Key, Object>> entries = mAdvancedExtender.getAvailableCharacteristicsKeyValues(); - if ((entries != null) && !entries.isEmpty()) { - CameraMetadataNative ret = new CameraMetadataNative(); - long vendorId = mMetadataVendorIdMap.containsKey(cameraId) - ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE; - ret.setVendorId(vendorId); - int[] characteristicsKeyTags = new int[entries.size()]; - int i = 0; - for (Pair<CameraCharacteristics.Key, Object> entry : entries) { - int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId); - characteristicsKeyTags[i++] = tag; - ret.set(entry.first, entry.second); - } - ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, - characteristicsKeyTags); + if (entries == null || entries.isEmpty()) { + throw new RuntimeException("A valid set of key/value pairs are required that " + + "are supported by the extension."); + } - return ret; + CameraMetadataNative ret = new CameraMetadataNative(); + long vendorId = mMetadataVendorIdMap.containsKey(cameraId) + ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE; + ret.setVendorId(vendorId); + int[] characteristicsKeyTags = new int[entries.size()]; + int i = 0; + for (Pair<CameraCharacteristics.Key, Object> entry : entries) { + int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId); + characteristicsKeyTags[i++] = tag; + ret.set(entry.first, entry.second); } + ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, + characteristicsKeyTags); + + return ret; } return null; diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 35ce4814f97d..132804f4f91d 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -24,6 +24,46 @@ java_library { visibility: ["//visibility:public"], } +java_library_host { + name: "ravenwood-helper-libcore-runtime.host", + srcs: [ + "runtime-helper-src/libcore-fake/**/*.java", + ], + visibility: ["//visibility:private"], +} + +java_host_for_device { + name: "ravenwood-helper-libcore-runtime", + libs: [ + "ravenwood-helper-libcore-runtime.host", + ], + visibility: ["//visibility:private"], +} + +java_library { + name: "ravenwood-helper-framework-runtime", + srcs: [ + "runtime-helper-src/framework/**/*.java", + ], + libs: [ + "framework-minus-apex.ravenwood", + ], + visibility: ["//visibility:private"], +} + +// Combine ravenwood-helper-*-runtime and create a single library, which we include +// in the ravenwood runtime. +// We do it this way rather than including the individual jars in the runtime, because +// for some reason we couldn't include a java_host_for_device module in the ravenwood runtime. +java_library { + name: "ravenwood-helper-runtime", + defaults: ["ravenwood-internal-only-visibility-java"], + static_libs: [ + "ravenwood-helper-framework-runtime", + "ravenwood-helper-libcore-runtime", + ], +} + java_library { name: "ravenwood-junit-impl", srcs: [ @@ -58,16 +98,6 @@ java_library { visibility: ["//visibility:public"], } -java_library { - // Prefixed with "200" to ensure it's sorted early in Tradefed classpath - // so that we provide a concrete implementation before Mainline stubs - name: "200-kxml2-android", - static_libs: [ - "kxml2-android", - ], - visibility: ["//frameworks/base"], -} - java_host_for_device { name: "androidx.test.monitor-for-device", libs: [ diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md index d84cb6795fef..4b2f96804c97 100644 --- a/ravenwood/api-maintainers.md +++ b/ravenwood/api-maintainers.md @@ -82,7 +82,7 @@ When a pure-Java implementation grows too large or complex to host within the or ``` @RavenwoodKeepWholeClass -@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.MyComplexClass_host") +@RavenwoodNativeSubstitutionClass("com.android.platform.test.ravenwood.nativesubstitution.MyComplexClass_host") public class MyComplexClass { private static native void nativeDoThing(long nativePtr); ... diff --git a/ravenwood/run-ravenwood-tests.sh b/ravenwood/run-ravenwood-tests.sh index 3f4b8a79e864..259aa702452d 100755 --- a/ravenwood/run-ravenwood-tests.sh +++ b/ravenwood/run-ravenwood-tests.sh @@ -13,10 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Run all the ravenwood tests. +# Run all the ravenwood tests + hoststubgen unit tests. + +all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test" # "echo" is to remove the newlines -all_tests=$(echo $(${0%/*}/list-ravenwood-tests.sh) ) +all_tests="$all_tests $(echo $(${0%/*}/list-ravenwood-tests.sh) )" echo "Running tests: $all_tests" atest $all_tests diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java index eba99107f126..f38d5653d3a9 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import android.database.Cursor; import android.database.sqlite.SQLiteException; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java index 6480cfc2b492..55d4ffb41e78 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import com.android.internal.os.RuntimeInit; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java index cdfa30276961..5930a14cdec8 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import android.util.Log; import android.util.Log.Level; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java index 4d39d88d58c3..741411095f53 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import android.os.BadParcelableException; import android.os.Parcel; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java index a5d0fc6872de..9486651ce48d 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongMultiStateCounter_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import android.os.BadParcelableException; import android.os.Parcel; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java index 65da4a144160..5e81124b6e70 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java index 0ebaac6016e2..2d799142df70 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import static android.os.ParcelFileDescriptor.MODE_APPEND; import static android.os.ParcelFileDescriptor.MODE_CREATE; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java index d63bff6f4da3..81ad31e631fe 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java index 2f6a361e3609..eba6c8b2db64 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.nativesubstitution; +package com.android.platform.test.ravenwood.nativesubstitution; import android.util.SparseArray; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java index fbcc64892798..1e120305fa2d 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.hoststubgen.runtimehelper; - -import com.android.hoststubgen.hosthelper.HostTestException; +package com.android.platform.test.ravenwood.runtimehelper; import java.io.File; import java.io.PrintStream; @@ -79,7 +77,7 @@ public class ClassLoadHook { private static void ensurePropertyNotSet(String key) { if (System.getProperty(key) != null) { - throw new HostTestException("System property \"" + key + "\" is set unexpectedly"); + throw new RuntimeException("System property \"" + key + "\" is set unexpectedly"); } } diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java index 388156aa3694..388156aa3694 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java +++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java index 379c4ae8a059..379c4ae8a059 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java +++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java index 7d2b00d9420d..7d2b00d9420d 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java +++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java index 65c285e06bf8..65c285e06bf8 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/EmptyArray.java index a1ae35a88656..a1ae35a88656 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/EmptyArray.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/HexEncoding.java index cc2fb7bbf236..cc2fb7bbf236 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/HexEncoding.java diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/SneakyThrow.java index e142c46bc311..e142c46bc311 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/SneakyThrow.java diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index 6ce471e635cd..fff283dd41bc 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -16,10 +16,9 @@ package com.android.server; +import static android.permission.flags.Flags.sensitiveNotificationAppProtection; import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS; - import static com.android.internal.util.Preconditions.checkNotNull; -import static com.android.server.notification.Flags.sensitiveNotificationAppProtection; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index a341b4acaca1..2e14abbd4d40 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -263,6 +263,10 @@ public class SystemConfig { // location settings are off, for emergency purposes, as read from the configuration files. final ArrayMap<String, ArraySet<String>> mAllowIgnoreLocationSettings = new ArrayMap<>(); + // These are the packages that are allow-listed to be able to access camera when + // the camera privacy state is for driver assistance apps only. + final ArrayMap<String, Boolean> mAllowlistCameraPrivacy = new ArrayMap<>(); + // These are the action strings of broadcasts which are whitelisted to // be delivered anonymously even to apps which target O+. final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>(); @@ -483,6 +487,10 @@ public class SystemConfig { return mAllowedAssociations; } + public ArrayMap<String, Boolean> getCameraPrivacyAllowlist() { + return mAllowlistCameraPrivacy; + } + public ArraySet<String> getBugreportWhitelistedPackages() { return mBugreportWhitelistedPackages; } @@ -1062,6 +1070,22 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "camera-privacy-allowlisted-app" : { + if (allowOverrideAppRestrictions) { + String pkgname = parser.getAttributeValue(null, "package"); + boolean isMandatory = XmlUtils.readBooleanAttribute( + parser, "mandatory", false); + if (pkgname == null) { + Slog.w(TAG, "<" + name + "> without package in " + + permFile + " at " + parser.getPositionDescription()); + } else { + mAllowlistCameraPrivacy.put(pkgname, isMandatory); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; case "allow-ignore-location-settings": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java new file mode 100644 index 000000000000..3312be231516 --- /dev/null +++ b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.adaptiveauth; + +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; + +import android.app.KeyguardManager; +import android.content.Context; +import android.hardware.biometrics.AuthenticationStateListener; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.BiometricSourceType; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Log; +import android.util.Slog; +import android.util.SparseIntArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockSettingsInternal; +import com.android.internal.widget.LockSettingsStateListener; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.pm.UserManagerInternal; +import com.android.server.wm.WindowManagerInternal; + +import java.util.Objects; + +/** + * @hide + */ +public class AdaptiveAuthService extends SystemService { + private static final String TAG = "AdaptiveAuthService"; + private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG); + + @VisibleForTesting + static final int MAX_ALLOWED_FAILED_AUTH_ATTEMPTS = 5; + private static final int MSG_REPORT_PRIMARY_AUTH_ATTEMPT = 1; + private static final int MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT = 2; + private static final int AUTH_SUCCESS = 1; + private static final int AUTH_FAILURE = 0; + + private final LockPatternUtils mLockPatternUtils; + private final LockSettingsInternal mLockSettings; + private final BiometricManager mBiometricManager; + private final KeyguardManager mKeyguardManager; + private final PowerManager mPowerManager; + private final WindowManagerInternal mWindowManager; + private final UserManagerInternal mUserManager; + @VisibleForTesting + final SparseIntArray mFailedAttemptsForUser = new SparseIntArray(); + + public AdaptiveAuthService(Context context) { + this(context, new LockPatternUtils(context)); + } + + @VisibleForTesting + public AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils) { + super(context); + mLockPatternUtils = lockPatternUtils; + mLockSettings = Objects.requireNonNull( + LocalServices.getService(LockSettingsInternal.class)); + mBiometricManager = Objects.requireNonNull( + context.getSystemService(BiometricManager.class)); + mKeyguardManager = Objects.requireNonNull(context.getSystemService(KeyguardManager.class)); + mPowerManager = Objects.requireNonNull(context.getSystemService(PowerManager.class)); + mWindowManager = Objects.requireNonNull( + LocalServices.getService(WindowManagerInternal.class)); + mUserManager = Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class)); + } + + @Override + public void onStart() {} + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + init(); + } + } + + @VisibleForTesting + void init() { + mLockSettings.registerLockSettingsStateListener(mLockSettingsStateListener); + mBiometricManager.registerAuthenticationStateListener(mAuthenticationStateListener); + } + + private final LockSettingsStateListener mLockSettingsStateListener = + new LockSettingsStateListener() { + @Override + public void onAuthenticationSucceeded(int userId) { + if (DEBUG) { + Slog.d(TAG, "LockSettingsStateListener#onAuthenticationSucceeded"); + } + mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_SUCCESS, userId) + .sendToTarget(); + } + + @Override + public void onAuthenticationFailed(int userId) { + Slog.i(TAG, "LockSettingsStateListener#onAuthenticationFailed"); + mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_FAILURE, userId) + .sendToTarget(); + } + }; + + private final AuthenticationStateListener mAuthenticationStateListener = + new AuthenticationStateListener.Stub() { + @Override + public void onAuthenticationStarted(int requestReason) {} + + @Override + public void onAuthenticationStopped() {} + + @Override + public void onAuthenticationSucceeded(int requestReason, int userId) { + if (DEBUG) { + Slog.d(TAG, "AuthenticationStateListener#onAuthenticationSucceeded"); + } + mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_SUCCESS, userId) + .sendToTarget(); + } + + @Override + public void onAuthenticationFailed(int requestReason, int userId) { + Slog.i(TAG, "AuthenticationStateListener#onAuthenticationFailed"); + mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_FAILURE, userId) + .sendToTarget(); + } + + @Override + public void onAuthenticationAcquired(BiometricSourceType biometricSourceType, + int requestReason, int acquiredInfo) {} + }; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REPORT_PRIMARY_AUTH_ATTEMPT: + handleReportPrimaryAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2); + break; + case MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT: + handleReportBiometricAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2); + break; + } + } + }; + + private void handleReportPrimaryAuthAttempt(boolean success, int userId) { + if (DEBUG) { + Slog.d(TAG, "handleReportPrimaryAuthAttempt: success=" + success + + ", userId=" + userId); + } + reportAuthAttempt(success, userId); + } + + private void handleReportBiometricAuthAttempt(boolean success, int userId) { + if (DEBUG) { + Slog.d(TAG, "handleReportBiometricAuthAttempt: success=" + success + + ", userId=" + userId); + } + reportAuthAttempt(success, userId); + } + + private void reportAuthAttempt(boolean success, int userId) { + if (success) { + // Deleting the entry effectively resets the counter of failed attempts for the user + mFailedAttemptsForUser.delete(userId); + return; + } + + final int numFailedAttempts = mFailedAttemptsForUser.get(userId, 0) + 1; + Slog.i(TAG, "reportAuthAttempt: numFailedAttempts=" + numFailedAttempts + + ", userId=" + userId); + mFailedAttemptsForUser.put(userId, numFailedAttempts); + + // Don't lock again if the device is already locked and if Keyguard is already showing and + // isn't trivially dismissible + if (mKeyguardManager.isDeviceLocked(userId) && mKeyguardManager.isKeyguardLocked()) { + Slog.d(TAG, "Not locking the device because the device is already locked."); + return; + } + + if (numFailedAttempts < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS) { + Slog.d(TAG, "Not locking the device because the number of failed attempts is below" + + " the threshold."); + return; + } + + //TODO: additionally consider the trust signal before locking device + lockDevice(userId); + } + + /** + * Locks the device and requires primary auth or biometric auth for unlocking + */ + private void lockDevice(int userId) { + // Require either primary auth or biometric auth to unlock the device again. Keyguard and + // bouncer will also check the StrongAuthFlag for the user to display correct strings for + // explaining why the device is locked + mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST, userId); + + // If userId is a profile that has a different parent userId (regardless of its profile + // type, or whether it's a profile with unified challenges or not), its parent userId that + // owns the Keyguard will also be locked + final int parentUserId = mUserManager.getProfileParentId(userId); + Slog.i(TAG, "lockDevice: userId=" + userId + ", parentUserId=" + parentUserId); + if (parentUserId != userId) { + mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST, + parentUserId); + } + + // Power off the display + mPowerManager.goToSleep(SystemClock.uptimeMillis()); + + // Lock the device + mWindowManager.lockNow(); + } +} diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index c85723525aa1..1dc384d61c91 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -383,6 +383,11 @@ public final class AppStartInfoTracker { start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid); start.setProcessName(app.processName); start.setPackageName(app.info.packageName); + if (android.content.pm.Flags.stayStopped()) { + // TODO: Verify this is created at the right time to have the correct force-stopped + // state in the ProcessRecord. Also use the WindowProcessRecord if activity. + start.setForceStopped(app.wasForceStopped()); + } } void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 7d82f0c2a63e..df46e5dafce8 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; @@ -67,7 +68,10 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE; import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; +import static android.media.audio.Flags.foregroundAudioControl; import static android.os.Process.SCHED_OTHER; import static android.os.Process.THREAD_GROUP_BACKGROUND; import static android.os.Process.THREAD_GROUP_DEFAULT; @@ -2266,6 +2270,15 @@ public class OomAdjuster { (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; + if (foregroundAudioControl()) { // flag check + final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + | FOREGROUND_SERVICE_TYPE_CAMERA + | FOREGROUND_SERVICE_TYPE_MICROPHONE + | FOREGROUND_SERVICE_TYPE_PHONE_CALL; + capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 + ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + } + final boolean enabled = state.getCachedCompatChange( CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY); if (enabled) { diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index f85b03e8b4eb..1bf779adcce1 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -722,14 +722,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { performNewUpdateOomAdjLSP(oomAdjReason, topApp, targetProcesses, activeUids, fullUpdate, now, UNKNOWN_ADJ); - if (fullUpdate) { - assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP()); - } else { - activeProcesses.clear(); - activeProcesses.addAll(targetProcesses); - assignCachedAdjIfNecessary(activeProcesses); - activeProcesses.clear(); - } + // TODO: b/319163103 - optimize cache adj assignment to not require the whole lru list. + assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP()); postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime); targetProcesses.clear(); @@ -996,11 +990,11 @@ public class OomAdjusterModernImpl extends OomAdjuster { && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ) || (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND - && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) { + && service.mState.getCurProcState() <= PROCESS_STATE_TOP) + || (service.isSdkSandbox && cr.binding.attributedClient != null)) { continue; } - computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false, oomAdjReason, cachedAdj, false, false); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d23d9fb16d6c..9883f091deef 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1678,7 +1678,11 @@ class ProcessRecord implements WindowProcessListener { final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j); for (int k = clist.size() - 1; k >= 0; k--) { final ConnectionRecord cr = clist.get(k); - consumer.accept(cr.binding.client); + if (isSdkSandbox && cr.binding.attributedClient != null) { + consumer.accept(cr.binding.attributedClient); + } else { + consumer.accept(cr.binding.client); + } } } } @@ -1689,25 +1693,5 @@ class ProcessRecord implements WindowProcessListener { consumer.accept(conn.client); } } - // If this process is a sandbox itself, also add the app on whose behalf - // its running - if (isSdkSandbox) { - for (int is = mServices.numberOfRunningServices() - 1; is >= 0; is--) { - ServiceRecord s = mServices.getRunningServiceAt(is); - ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = - s.getConnections(); - for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) { - ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni); - for (int i = clist.size() - 1; i >= 0; i--) { - ConnectionRecord cr = clist.get(i); - ProcessRecord attributedApp = cr.binding.attributedClient; - if (attributedApp == null || attributedApp == this) { - continue; - } - consumer.accept(attributedApp); - } - } - } - } } } diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index 57d233e7c503..562beaf50a7f 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -205,10 +205,10 @@ final class ProcessServiceRecord { } /** - * Returns the FGS typps, but it doesn't tell if the types include "NONE" or not, so - * do not use it outside of this class. + * Returns the FGS types, but it doesn't tell if the types include "NONE" or not, use + * {@link #hasForegroundServices()} */ - private int getForegroundServiceTypes() { + int getForegroundServiceTypes() { return mHasForegroundServices ? mFgServiceTypes : 0; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 55ac4cf37283..34ba7f0debb0 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -97,6 +97,7 @@ import android.os.IProgressListener; import android.os.IRemoteCallback; import android.os.IUserManager; import android.os.Message; +import android.os.PowerManagerInternal; import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -1934,9 +1935,12 @@ class UserController implements Handler.Callback { } /** - * Start user, if its not already running, and bring it to foreground. + * Start user, if it's not already running, and bring it to foreground. */ void startUserInForeground(@UserIdInt int targetUserId) { + if (android.multiuser.Flags.setPowerModeDuringUserSwitch()) { + mInjector.setPerformancePowerMode(true); + } boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND); if (!success) { mInjector.getWindowManager().setSwitchingUser(false); @@ -2146,6 +2150,9 @@ class UserController implements Handler.Callback { } private void endUserSwitch() { + if (android.multiuser.Flags.setPowerModeDuringUserSwitch()) { + mInjector.setPerformancePowerMode(false); + } final int nextUserId; synchronized (mLock) { nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL); @@ -3535,6 +3542,7 @@ class UserController implements Handler.Callback { private final ActivityManagerService mService; private UserManagerService mUserManager; private UserManagerInternal mUserManagerInternal; + private PowerManagerInternal mPowerManagerInternal; private Handler mHandler; private final Object mUserSwitchingDialogLock = new Object(); @GuardedBy("mUserSwitchingDialogLock") @@ -3636,6 +3644,13 @@ class UserController implements Handler.Callback { return mUserManagerInternal; } + PowerManagerInternal getPowerManagerInternal() { + if (mPowerManagerInternal == null) { + mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); + } + return mPowerManagerInternal; + } + KeyguardManager getKeyguardManager() { return mService.mContext.getSystemService(KeyguardManager.class); } @@ -3829,6 +3844,12 @@ class UserController implements Handler.Callback { getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId); } + void setPerformancePowerMode(boolean enabled) { + Slogf.i(TAG, "Setting power mode MODE_FIXED_PERFORMANCE to " + enabled); + getPowerManagerInternal().setPowerMode( + PowerManagerInternal.MODE_FIXED_PERFORMANCE, enabled); + } + void onSystemUserVisibilityChanged(boolean visible) { getUserManagerInternal().onSystemUserVisibilityChanged(visible); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 5c95d433a6c1..fca119917fd0 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -106,6 +106,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; +import android.hardware.SensorPrivacyManager; import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; import android.net.Uri; import android.os.AsyncTask; @@ -151,6 +152,7 @@ import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsStartedCallback; import com.android.internal.app.MessageSamplingConfig; +import com.android.internal.camera.flags.Flags; import com.android.internal.compat.IPlatformCompat; import com.android.internal.os.Clock; import com.android.internal.pm.pkg.component.ParsedAttribution; @@ -164,7 +166,6 @@ import com.android.modules.utils.TypedXmlSerializer; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.LockGuard; -import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemServiceManager; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.pm.PackageList; @@ -223,6 +224,8 @@ public class AppOpsService extends IAppOpsService.Stub { */ private static final int CURRENT_VERSION = 1; + private SensorPrivacyManager mSensorPrivacyManager; + // Write at most every 30 minutes. static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000; @@ -655,11 +658,11 @@ public class AppOpsService extends IAppOpsService.Stub { return attributedOp; } - @NonNull OpEntry createEntryLocked() { + @NonNull OpEntry createEntryLocked(String persistentDeviceId) { // TODO(b/308201969): Update this method when we introduce disk persistence of events // for accesses on external devices. final ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get( - PERSISTENT_DEVICE_ID_DEFAULT); + persistentDeviceId); final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries = new ArrayMap<>(attributedOps.size()); for (int i = 0; i < attributedOps.size(); i++) { @@ -1034,7 +1037,7 @@ public class AppOpsService extends IAppOpsService.Stub { new Ops(pkgName, uidState)); } - createSandboxUidStateIfNotExistsForAppLocked(uid); + createSandboxUidStateIfNotExistsForAppLocked(uid, null); } } else if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) { synchronized (AppOpsService.this) { @@ -1046,69 +1049,8 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - ArrayMap<String, String> dstAttributionTags = new ArrayMap<>(); - ArraySet<String> attributionTags = new ArraySet<>(); - attributionTags.add(null); - if (pkg.getAttributions() != null) { - int numAttributions = pkg.getAttributions().size(); - for (int attributionNum = 0; attributionNum < numAttributions; - attributionNum++) { - ParsedAttribution attribution = pkg.getAttributions().get(attributionNum); - attributionTags.add(attribution.getTag()); - - int numInheritFrom = attribution.getInheritFrom().size(); - for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; - inheritFromNum++) { - dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum), - attribution.getTag()); - } - } - } - synchronized (AppOpsService.this) { - UidState uidState = mUidStates.get(uid); - if (uidState == null) { - return; - } - - Ops ops = uidState.pkgOps.get(pkgName); - if (ops == null) { - return; - } - - // Reset cached package properties to re-initialize when needed - ops.bypass = null; - ops.knownAttributionTags.clear(); - - // Merge data collected for removed attributions into their successor - // attributions - int numOps = ops.size(); - for (int opNum = 0; opNum < numOps; opNum++) { - Op op = ops.valueAt(opNum); - for (int deviceIndex = op.mDeviceAttributedOps.size() - 1; deviceIndex >= 0; - deviceIndex--) { - ArrayMap<String, AttributedOp> attributedOps = - op.mDeviceAttributedOps.valueAt(deviceIndex); - for (int tagIndex = attributedOps.size() - 1; tagIndex >= 0; - tagIndex--) { - String tag = attributedOps.keyAt(tagIndex); - if (attributionTags.contains(tag)) { - // attribution still exist after upgrade - continue; - } - - String newAttributionTag = dstAttributionTags.get(tag); - - AttributedOp newAttributedOp = op.getOrCreateAttribution(op, - newAttributionTag, - op.mDeviceAttributedOps.keyAt(deviceIndex)); - newAttributedOp.add(attributedOps.get(tag)); - attributedOps.remove(tag); - - scheduleFastWriteLocked(); - } - } - } + refreshAttributionsLocked(pkg, uid); } } } @@ -1132,41 +1074,6 @@ public class AppOpsService extends IAppOpsService.Stub { mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL, packageUpdateFilter, null, null); - synchronized (this) { - for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) { - int uid = mUidStates.keyAt(uidNum); - UidState uidState = mUidStates.valueAt(uidNum); - - String[] pkgsInUid = getPackagesForUid(uidState.uid); - if (ArrayUtils.isEmpty(pkgsInUid) && uid >= Process.FIRST_APPLICATION_UID) { - uidState.clear(); - mUidStates.removeAt(uidNum); - scheduleFastWriteLocked(); - continue; - } - - ArrayMap<String, Ops> pkgs = uidState.pkgOps; - - int numPkgs = pkgs.size(); - for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) { - String pkg = pkgs.keyAt(pkgNum); - - String action; - if (!ArrayUtils.contains(pkgsInUid, pkg)) { - action = ACTION_PACKAGE_REMOVED; - } else { - action = Intent.ACTION_PACKAGE_REPLACED; - } - - SystemServerInitThreadPool.submit( - () -> mOnPackageUpdatedReceiver.onReceive(mContext, new Intent(action) - .setData(Uri.fromParts("package", pkg, null)) - .putExtra(Intent.EXTRA_UID, uid)), - "Update app-ops uidState in case package " + pkg + " changed"); - } - } - } - prepareInternalCallbacks(); final IntentFilter packageSuspendFilter = new IntentFilter(); @@ -1231,6 +1138,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } }); + mSensorPrivacyManager = SensorPrivacyManager.getInstance(mContext); } @VisibleForTesting @@ -1253,20 +1161,27 @@ public class AppOpsService extends IAppOpsService.Stub { void initializeUidStates() { UserManagerInternal umi = getUserManagerInternal(); synchronized (this) { + SparseBooleanArray knownUids = new SparseBooleanArray(); + + for (int uid : NON_PACKAGE_UIDS) { + if (!mUidStates.contains(uid)) { + mUidStates.put(uid, new UidState(uid)); + } + knownUids.put(uid, true); + } + int[] userIds = umi.getUserIds(); try (PackageManagerLocal.UnfilteredSnapshot snapshot = getPackageManagerLocal().withUnfilteredSnapshot()) { Map<String, PackageState> packageStates = snapshot.getPackageStates(); for (int i = 0; i < userIds.length; i++) { int userId = userIds[i]; - initializeUserUidStatesLocked(userId, packageStates); + initializeUserUidStatesLocked(userId, packageStates, knownUids); } - } - for (int uid : NON_PACKAGE_UIDS) { - mUidStates.put(uid, new UidState(uid)); + trimUidStatesLocked(knownUids, packageStates); + mUidStatesInitialized = true; } - mUidStatesInitialized = true; } } @@ -1274,26 +1189,34 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { try (PackageManagerLocal.UnfilteredSnapshot snapshot = getPackageManagerLocal().withUnfilteredSnapshot()) { - initializeUserUidStatesLocked(userId, snapshot.getPackageStates()); + initializeUserUidStatesLocked(userId, snapshot.getPackageStates(), null); } } } private void initializeUserUidStatesLocked(int userId, Map<String, - PackageState> packageStates) { + PackageState> packageStates, SparseBooleanArray knownUids) { for (Map.Entry<String, PackageState> entry : packageStates.entrySet()) { - int appId = entry.getValue().getAppId(); + PackageState packageState = entry.getValue(); + if (packageState.isApex()) { + continue; + } + int appId = packageState.getAppId(); String packageName = entry.getKey(); - initializePackageUidStateLocked(userId, appId, packageName); + initializePackageUidStateLocked(userId, appId, packageName, knownUids); } } /* Be careful not to clear any existing data; only want to add objects that don't already exist. */ - private void initializePackageUidStateLocked(int userId, int appId, String packageName) { + private void initializePackageUidStateLocked(int userId, int appId, String packageName, + SparseBooleanArray knownUids) { int uid = UserHandle.getUid(userId, appId); + if (knownUids != null) { + knownUids.put(uid, true); + } UidState uidState = getUidStateLocked(uid, true); Ops ops = uidState.pkgOps.get(packageName); if (ops == null) { @@ -1311,7 +1234,105 @@ public class AppOpsService extends IAppOpsService.Stub { } } - createSandboxUidStateIfNotExistsForAppLocked(uid); + createSandboxUidStateIfNotExistsForAppLocked(uid, knownUids); + } + + private void trimUidStatesLocked(SparseBooleanArray knownUids, + Map<String, PackageState> packageStates) { + synchronized (this) { + // Remove what may have been added during persistence parsing + for (int i = mUidStates.size() - 1; i >= 0; i--) { + int uid = mUidStates.keyAt(i); + if (knownUids.get(uid, false)) { + if (uid >= Process.FIRST_APPLICATION_UID) { + ArrayMap<String, Ops> pkgOps = mUidStates.valueAt(i).pkgOps; + for (int j = 0; j < pkgOps.size(); j++) { + String pkgName = pkgOps.keyAt(j); + if (!packageStates.containsKey(pkgName)) { + pkgOps.removeAt(j); + continue; + } + AndroidPackage pkg = packageStates.get(pkgName).getAndroidPackage(); + if (pkg != null) { + refreshAttributionsLocked(pkg, uid); + } + } + if (pkgOps.isEmpty()) { + mUidStates.remove(i); + } + } + } else { + mUidStates.removeAt(i); + } + } + } + } + + @GuardedBy("this") + private void refreshAttributionsLocked(AndroidPackage pkg, int uid) { + String pkgName = pkg.getPackageName(); + ArrayMap<String, String> dstAttributionTags = new ArrayMap<>(); + ArraySet<String> attributionTags = new ArraySet<>(); + attributionTags.add(null); + if (pkg.getAttributions() != null) { + int numAttributions = pkg.getAttributions().size(); + for (int attributionNum = 0; attributionNum < numAttributions; + attributionNum++) { + ParsedAttribution attribution = pkg.getAttributions().get(attributionNum); + attributionTags.add(attribution.getTag()); + + int numInheritFrom = attribution.getInheritFrom().size(); + for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; + inheritFromNum++) { + dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum), + attribution.getTag()); + } + } + } + + UidState uidState = mUidStates.get(uid); + if (uidState == null) { + return; + } + + Ops ops = uidState.pkgOps.get(pkgName); + if (ops == null) { + return; + } + + // Reset cached package properties to re-initialize when needed + ops.bypass = null; + ops.knownAttributionTags.clear(); + + // Merge data collected for removed attributions into their successor + // attributions + int numOps = ops.size(); + for (int opNum = 0; opNum < numOps; opNum++) { + Op op = ops.valueAt(opNum); + for (int deviceIndex = op.mDeviceAttributedOps.size() - 1; deviceIndex >= 0; + deviceIndex--) { + ArrayMap<String, AttributedOp> attributedOps = + op.mDeviceAttributedOps.valueAt(deviceIndex); + for (int tagIndex = attributedOps.size() - 1; tagIndex >= 0; + tagIndex--) { + String tag = attributedOps.keyAt(tagIndex); + if (attributionTags.contains(tag)) { + // attribution still exist after upgrade + continue; + } + + String newAttributionTag = dstAttributionTags.get(tag); + + AttributedOp newAttributedOp = op.getOrCreateAttribution(op, + newAttributionTag, + op.mDeviceAttributedOps.keyAt(deviceIndex)); + newAttributedOp.add(attributedOps.get(tag)); + attributedOps.remove(tag); + + scheduleFastWriteLocked(); + } + } + } } /** @@ -1529,13 +1550,14 @@ public class AppOpsService extends IAppOpsService.Stub { mHistoricalRegistry.shutdown(); } - private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) { + private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops, + String persistentDeviceId) { ArrayList<AppOpsManager.OpEntry> resOps = null; if (ops == null) { resOps = new ArrayList<>(); for (int j=0; j<pkgOps.size(); j++) { Op curOp = pkgOps.valueAt(j); - resOps.add(getOpEntryForResult(curOp)); + resOps.add(getOpEntryForResult(curOp, persistentDeviceId)); } } else { for (int j=0; j<ops.length; j++) { @@ -1544,7 +1566,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (resOps == null) { resOps = new ArrayList<>(); } - resOps.add(getOpEntryForResult(curOp)); + resOps.add(getOpEntryForResult(curOp, persistentDeviceId)); } } } @@ -1588,16 +1610,23 @@ public class AppOpsService extends IAppOpsService.Stub { return resOps; } - private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) { - return op.createEntryLocked(); + private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, String persistentDeviceId) { + return op.createEntryLocked(persistentDeviceId); } @Override public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) { + return getPackagesForOpsForDevice(ops, PERSISTENT_DEVICE_ID_DEFAULT); + } + + @Override + public List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(int[] ops, + @NonNull String persistentDeviceId) { final int callingUid = Binder.getCallingUid(); final boolean hasAllPackageAccess = mContext.checkPermission( Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED; + ArrayList<AppOpsManager.PackageOps> res = null; synchronized (this) { final int uidStateCount = mUidStates.size(); @@ -1606,21 +1635,24 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState.pkgOps.isEmpty()) { continue; } + // Caller can always see their packages and with a permission all. + if (!hasAllPackageAccess && callingUid != uidState.uid) { + continue; + } + ArrayMap<String, Ops> packages = uidState.pkgOps; final int packageCount = packages.size(); for (int j = 0; j < packageCount; j++) { Ops pkgOps = packages.valueAt(j); - ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops); + ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops, + persistentDeviceId); if (resOps != null) { if (res == null) { res = new ArrayList<>(); } AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps( pkgOps.packageName, pkgOps.uidState.uid, resOps); - // Caller can always see their packages and with a permission all. - if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) { - res.add(resPackage); - } + res.add(resPackage); } } } @@ -1642,7 +1674,8 @@ public class AppOpsService extends IAppOpsService.Stub { if (pkgOps == null) { return null; } - ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops); + ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops, + PERSISTENT_DEVICE_ID_DEFAULT); if (resOps == null || resOps.size() == 0) { return null; } @@ -4246,8 +4279,15 @@ public class AppOpsService extends IAppOpsService.Stub { return uidState; } - private void createSandboxUidStateIfNotExistsForAppLocked(int uid) { + private void createSandboxUidStateIfNotExistsForAppLocked(int uid, + SparseBooleanArray knownUids) { + if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) { + return; + } final int sandboxUid = Process.toSdkSandboxUid(uid); + if (knownUids != null) { + knownUids.put(sandboxUid, true); + } getUidStateLocked(sandboxUid, true); } @@ -4642,6 +4682,10 @@ public class AppOpsService extends IAppOpsService.Stub { return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid)); } + private boolean isAutomotive() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } + private boolean isOpRestrictedLocked(int uid, int code, String packageName, String attributionTag, int virtualDeviceId, @Nullable RestrictionBypass appBypass, boolean isCheckOp) { @@ -4658,6 +4702,13 @@ public class AppOpsService extends IAppOpsService.Stub { } } + if ((code == OP_CAMERA) && isAutomotive()) { + if ((Flags.cameraPrivacyAllowlist()) + && (mSensorPrivacyManager.isCameraPrivacyEnabled(packageName))) { + return true; + } + } + int userHandle = UserHandle.getUserId(uid); restrictionSetCount = mOpUserRestrictions.size(); diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java index 23a384ff5d3b..bc6ef2005584 100644 --- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java +++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java @@ -16,6 +16,7 @@ package com.android.server.appop; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; @@ -30,6 +31,7 @@ import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS; import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.UID_STATE_TOP; @@ -139,7 +141,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { } private int evalModeInternal(int uid, int code, int uidState, int uidCapability) { - if (getUidAppWidgetVisible(uid) || mActivityManagerInternal.isPendingTopUid(uid) || mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) { return MODE_ALLOWED; @@ -173,6 +174,8 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { case OP_RECORD_AUDIO: case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO: return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; + case OP_TAKE_AUDIO_FOCUS: + return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; default: return PROCESS_CAPABILITY_NONE; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c59f4f7888ce..559a1d647ea4 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -34,6 +34,7 @@ import static android.media.audio.Flags.autoPublicVolumeApiHardening; import static android.media.audio.Flags.automaticBtDeviceType; import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency; import static android.media.audio.Flags.focusFreezeTestApi; +import static android.media.audio.Flags.foregroundAudioControl; import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.INVALID_UID; @@ -1356,7 +1357,8 @@ public class AudioService extends IAudioService.Stub mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler); - mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive()); + mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive(), mAppOps, + context.getPackageManager()); } private void initVolumeStreamStates() { @@ -4517,7 +4519,8 @@ public class AudioService extends IAudioService.Stub } private void dumpFlags(PrintWriter pw) { - pw.println("\nFun with Flags: "); + + pw.println("\nFun with Flags:"); pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:" + autoPublicVolumeApiHardening()); pw.println("\tandroid.media.audio.Flags.automaticBtDeviceType:" @@ -4528,8 +4531,8 @@ public class AudioService extends IAudioService.Stub + focusFreezeTestApi()); pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:" + disablePrescaleAbsoluteVolume()); - pw.println("\tandroid.media.audiopolicy.enableFadeManagerConfiguration:" - + enableFadeManagerConfiguration()); + pw.println("\tandroid.media.audio.foregroundAudioControl:" + + foregroundAudioControl()); } private void dumpAudioMode(PrintWriter pw) { @@ -10175,10 +10178,38 @@ public class AudioService extends IAudioService.Stub .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } + + // does caller have system privileges to bypass HardeningEnforcer + boolean permissionOverridesCheck = false; + if ((mContext.checkCallingOrSelfPermission( + Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + == PackageManager.PERMISSION_GRANTED) + || (mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_ROUTING) + == PackageManager.PERMISSION_GRANTED)) { + permissionOverridesCheck = true; + } else if (uid < UserHandle.AID_APP_START) { + permissionOverridesCheck = true; + } + + final long token = Binder.clearCallingIdentity(); + try { + if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid, + HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS, + clientId, durationHint, callingPackageName)) { + final String reason = "Audio focus request blocked by hardening"; + Log.w(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record(); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + } finally { + Binder.restoreCallingIdentity(token); + } + mmi.record(); return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, attributionTag, flags, sdk, - forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/); + forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/, + permissionOverridesCheck); } /** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */ @@ -10195,7 +10226,7 @@ public class AudioService extends IAudioService.Stub } return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, null, flags, - sdk, false /*forceDuck*/, fakeUid); + sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, @@ -11639,6 +11670,7 @@ public class AudioService extends IAudioService.Stub pw.println("\nMessage handler is null"); } dumpFlags(pw); + mHardeningEnforcer.dump(pw); mMediaFocusControl.dump(pw); dumpStreamStates(pw); dumpVolumeGroups(pw); diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java index 4ceb83b2e1c9..409ed17001b7 100644 --- a/services/core/java/com/android/server/audio/HardeningEnforcer.java +++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java @@ -18,13 +18,21 @@ package com.android.server.audio; import static android.media.audio.Flags.autoPublicVolumeApiHardening; import android.Manifest; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; + +import com.android.server.utils.EventLogger; + +import java.io.PrintWriter; /** * Class to encapsulate all audio API hardening operations @@ -32,10 +40,19 @@ import android.util.Log; public class HardeningEnforcer { private static final String TAG = "AS.HardeningEnforcer"; + private static final boolean DEBUG = false; + private static final int LOG_NB_EVENTS = 20; final Context mContext; + final AppOpsManager mAppOps; final boolean mIsAutomotive; + final ActivityManager mActivityManager; + final PackageManager mPackageManager; + + final EventLogger mEventLogger = new EventLogger(LOG_NB_EVENTS, + "Hardening enforcement"); + /** * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)} */ @@ -56,10 +73,24 @@ public class HardeningEnforcer { * Matches calls from {@link AudioManager#setRingerMode(int)} */ public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200; + /** + * Matches calls from {@link AudioManager#requestAudioFocus(AudioFocusRequest)} + * and legacy variants + */ + public static final int METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS = 300; - public HardeningEnforcer(Context ctxt, boolean isAutomotive) { + public HardeningEnforcer(Context ctxt, boolean isAutomotive, AppOpsManager appOps, + PackageManager pm) { mContext = ctxt; mIsAutomotive = isAutomotive; + mAppOps = appOps; + mActivityManager = ctxt.getSystemService(ActivityManager.class); + mPackageManager = pm; + } + + protected void dump(PrintWriter pw) { + // log + mEventLogger.dump(pw); } /** @@ -84,7 +115,7 @@ public class HardeningEnforcer { } // TODO metrics? // TODO log for audio dumpsys? - Log.e(TAG, "Preventing volume method " + volumeMethod + " for " + Slog.e(TAG, "Preventing volume method " + volumeMethod + " for " + getPackNameForUid(Binder.getCallingUid())); return true; } @@ -92,10 +123,40 @@ public class HardeningEnforcer { return false; } + /** + * Checks whether the call in the current thread should be allowed or blocked + * @param focusMethod name of the method to check, for logging purposes + * @param clientId id of the requester + * @param durationHint focus type being requested + * @return false if the method call is allowed, true if it should be a no-op + */ + protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId, + int durationHint, @NonNull String packageName) { + if (packageName.isEmpty()) { + packageName = getPackNameForUid(callingUid); + } + + if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) { + if (DEBUG) { + Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking"); + } + return false; + } + + String errorMssg = "Focus request DENIED for uid:" + callingUid + + " clientId:" + clientId + " req:" + durationHint + + " procState:" + mActivityManager.getUidProcessState(callingUid); + + // TODO metrics + mEventLogger.enqueueAndSlog(errorMssg, EventLogger.Event.ALOGI, TAG); + + return true; + } + private String getPackNameForUid(int uid) { final long token = Binder.clearCallingIdentity(); try { - final String[] names = mContext.getPackageManager().getPackagesForUid(uid); + final String[] names = mPackageManager.getPackagesForUid(uid); if (names == null || names.length == 0 || TextUtils.isEmpty(names[0])) { @@ -106,4 +167,18 @@ public class HardeningEnforcer { Binder.restoreCallingIdentity(token); } } + + /** + * Checks the given op without throwing + * @param op the appOp code + * @param uid the calling uid + * @param packageName the package name of the caller + * @return return false if the operation is not allowed + */ + private boolean checkAppOp(int op, int uid, @NonNull String packageName) { + if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) { + return false; + } + return true; + } } diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 1376bde2fb71..35d38e2373f5 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -1090,11 +1090,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * accessibility. * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST * otherwise the UID being injected for testing + * @param permissionOverridesCheck true if permission checks guaranteed that the call should + * go through, false otherwise (e.g. non-privileged caller) * @return */ protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, - String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) { + String attributionTag, int flags, int sdk, boolean forceDuck, int testUid, + boolean permissionOverridesCheck) { new MediaMetrics.Item(mMetricsId) .setUid(Binder.getCallingUid()) .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) @@ -1126,10 +1129,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST) - // note we're using the real uid for appOp evaluation - && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), - callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) { + final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), + callingPackageName, attributionTag, null); + if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1ca3923b325c..d5863a73a0c3 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1638,21 +1638,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // SDR brightness is unchanged, so animate quickly as this is only impacting // a likely minority amount of display content // ie, the highlights of an HDR video or UltraHDR image + // Ideally we'd do this as fast as possible (ie, skip the animation entirely), + // but this requires display support and would need an entry in the + // display configuration. For now just do the fast animation slowChange = false; - - // Going from HDR to no HDR; visually this should be a "no-op" anyway - // as the remaining SDR content's brightness should be holding steady - // due to the sdr brightness not shifting - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) { - skipAnimation = true; - } - - // Going from no HDR to HDR; visually this is a significant scene change - // and the animation just prevents advanced clients from doing their own - // handling of enter/exit animations if they would like to do such a thing - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) { - skipAnimation = true; - } } if (skipAnimation) { animateScreenBrightness(animateValue, sdrAnimateValue, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 4bb8e1913199..bc169ca40117 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2901,16 +2901,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void clearClientSessionsLocked() { if (getCurMethodLocked() != null) { - // TODO(b/322816970): Replace this with lambda. - mClientController.forAllClients(new Consumer<ClientState>() { - - @GuardedBy("ImfLock.class") - @Override - public void accept(ClientState c) { - clearClientSessionLocked(c); - clearClientSessionForAccessibilityLocked(c); - } - }); + // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. + @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> { + clearClientSessionLocked(c); + clearClientSessionForAccessibilityLocked(c); + }; + mClientController.forAllClients(clearClientSession); finishSessionLocked(mEnabledSession); for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { @@ -4653,15 +4649,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.startImeTrace_enforcePermission(); ImeTracing.getInstance().startTrace(null /* printwriter */); synchronized (ImfLock.class) { - // TODO(b/322816970): Replace this with lambda. - mClientController.forAllClients(new Consumer<ClientState>() { - - @GuardedBy("ImfLock.class") - @Override - public void accept(ClientState c) { - c.mClient.setImeTraceEnabled(true /* enabled */); - } - }); + mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(true /* enabled */)); } } @@ -4673,15 +4661,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ImeTracing.getInstance().stopTrace(null /* printwriter */); synchronized (ImfLock.class) { - // TODO(b/322816970): Replace this with lambda. - mClientController.forAllClients(new Consumer<ClientState>() { - - @GuardedBy("ImfLock.class") - @Override - public void accept(ClientState c) { - c.mClient.setImeTraceEnabled(false /* enabled */); - } - }); + mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(false /* enabled */)); } } @@ -5916,15 +5896,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We only have sessions when we bound to an input method. Remove this session // from all clients. if (getCurMethodLocked() != null) { - // TODO(b/322816970): Replace this with lambda. - mClientController.forAllClients(new Consumer<ClientState>() { + // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. + @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = + c -> clearClientSessionForAccessibilityLocked(c, + accessibilityConnectionId); + mClientController.forAllClients(clearClientSession); - @GuardedBy("ImfLock.class") - @Override - public void accept(ClientState c) { - clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); - } - }); AccessibilitySessionState session = mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { @@ -6126,24 +6103,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } // Dump ClientController#mClients p.println(" ClientStates:"); - // TODO(b/322816970): Replace this with lambda. - mClientController.forAllClients(new Consumer<ClientState>() { + // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed. + @SuppressWarnings("GuardedBy") Consumer<ClientState> clientControllerDump = c -> { + p.println(" " + c + ":"); + p.println(" client=" + c.mClient); + p.println(" fallbackInputConnection=" + + c.mFallbackInputConnection); + p.println(" sessionRequested=" + + c.mSessionRequested); + p.println( + " sessionRequestedForAccessibility=" + + c.mSessionRequestedForAccessibility); + p.println(" curSession=" + c.mCurSession); + }; + mClientController.forAllClients(clientControllerDump); - @GuardedBy("ImfLock.class") - @Override - public void accept(ClientState c) { - p.println(" " + c + ":"); - p.println(" client=" + c.mClient); - p.println(" fallbackInputConnection=" - + c.mFallbackInputConnection); - p.println(" sessionRequested=" - + c.mSessionRequested); - p.println( - " sessionRequestedForAccessibility=" - + c.mSessionRequestedForAccessibility); - p.println(" curSession=" + c.mCurSession); - } - }); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java deleted file mode 100644 index ac42646499a3..000000000000 --- a/services/core/java/com/android/server/location/GeocoderProxy.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2010 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.location; - -import android.annotation.Nullable; -import android.content.Context; -import android.location.GeocoderParams; -import android.location.IGeocodeListener; -import android.location.IGeocodeProvider; -import android.os.IBinder; -import android.os.RemoteException; - -import com.android.server.servicewatcher.CurrentUserServiceSupplier; -import com.android.server.servicewatcher.ServiceWatcher; - -import java.util.Collections; - -/** - * Proxy for IGeocodeProvider implementations. - * - * @hide - */ -public class GeocoderProxy { - - private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider"; - - /** - * Creates and registers this proxy. If no suitable service is available for the proxy, returns - * null. - */ - @Nullable - public static GeocoderProxy createAndRegister(Context context) { - GeocoderProxy proxy = new GeocoderProxy(context); - if (proxy.register()) { - return proxy; - } else { - return null; - } - } - - private final ServiceWatcher mServiceWatcher; - - private GeocoderProxy(Context context) { - mServiceWatcher = ServiceWatcher.create(context, "GeocoderProxy", - CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION, - com.android.internal.R.bool.config_enableGeocoderOverlay, - com.android.internal.R.string.config_geocoderProviderPackageName), - null); - } - - private boolean register() { - boolean resolves = mServiceWatcher.checkServiceResolves(); - if (resolves) { - mServiceWatcher.register(); - } - return resolves; - } - - /** - * Geocodes stuff. - */ - public void getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - provider.getFromLocation(latitude, longitude, maxResults, params, listener); - } - - @Override - public void onError(Throwable t) { - try { - listener.onResults(t.toString(), Collections.emptyList()); - } catch (RemoteException e) { - // ignore - } - } - }); - } - - /** - * Geocodes stuff. - */ - public void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - provider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, listener); - } - - @Override - public void onError(Throwable t) { - try { - listener.onResults(t.toString(), Collections.emptyList()); - } catch (RemoteException e) { - // ignore - } - } - }); - } -} diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 323cdc54edae..c5f38553ed81 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -52,13 +52,11 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Criteria; -import android.location.GeocoderParams; import android.location.Geofence; import android.location.GnssAntennaInfo; import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementRequest; -import android.location.IGeocodeListener; import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; @@ -75,8 +73,11 @@ import android.location.LocationManagerInternal.LocationPackageTagsListener; import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.IGeocodeCallback; import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; +import android.location.provider.ReverseGeocodeRequest; import android.location.util.identity.CallerIdentity; import android.os.Binder; import android.os.Build; @@ -141,6 +142,7 @@ import com.android.server.location.provider.MockLocationProvider; import com.android.server.location.provider.PassiveLocationProvider; import com.android.server.location.provider.PassiveLocationProviderManager; import com.android.server.location.provider.StationaryThrottlingLocationProvider; +import com.android.server.location.provider.proxy.ProxyGeocodeProvider; import com.android.server.location.provider.proxy.ProxyLocationProvider; import com.android.server.location.settings.LocationSettings; import com.android.server.location.settings.LocationUserSettings; @@ -253,7 +255,7 @@ public class LocationManagerService extends ILocationManager.Stub implements private final GeofenceManager mGeofenceManager; private volatile @Nullable GnssManagerService mGnssManagerService = null; - private GeocoderProxy mGeocodeProvider; + private ProxyGeocodeProvider mGeocodeProvider; private final Object mDeprecatedGnssBatchingLock = new Object(); @GuardedBy("mDeprecatedGnssBatchingLock") @@ -493,7 +495,7 @@ public class LocationManagerService extends ILocationManager.Stub implements } // bind to geocoder provider - mGeocodeProvider = GeocoderProxy.createAndRegister(mContext); + mGeocodeProvider = ProxyGeocodeProvider.createAndRegister(mContext); if (mGeocodeProvider == null) { Log.e(TAG, "no geocoder provider found"); } @@ -1363,23 +1365,22 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public boolean geocoderIsPresent() { + public boolean isGeocodeAvailable() { return mGeocodeProvider != null; } @Override - public void getFromLocation(double latitude, double longitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - // validate identity - CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(), - params.getClientAttributionTag()); - Preconditions.checkArgument(identity.getUid() == params.getClientUid()); + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); if (mGeocodeProvider != null) { - mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, params, listener); + mGeocodeProvider.reverseGeocode(request, callback); } else { try { - listener.onResults(null, Collections.emptyList()); + callback.onError(null); } catch (RemoteException e) { // ignore } @@ -1387,22 +1388,17 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public void getFromLocationName(String locationName, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, - GeocoderParams params, IGeocodeListener listener) { - // validate identity - CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(), - params.getClientAttributionTag()); - Preconditions.checkArgument(identity.getUid() == params.getClientUid()); + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); if (mGeocodeProvider != null) { - mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, listener); + mGeocodeProvider.forwardGeocode(request, callback); } else { try { - listener.onResults(null, Collections.emptyList()); + callback.onError(null); } catch (RemoteException e) { // ignore } diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java new file mode 100644 index 000000000000..ac945f1d2921 --- /dev/null +++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 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.location.provider.proxy; + +import android.annotation.Nullable; +import android.content.Context; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.GeocodeProviderBase; +import android.location.provider.IGeocodeCallback; +import android.location.provider.IGeocodeProvider; +import android.location.provider.ReverseGeocodeRequest; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.server.servicewatcher.CurrentUserServiceSupplier; +import com.android.server.servicewatcher.ServiceWatcher; + +/** Proxy for IGeocodeProvider implementations. */ +public class ProxyGeocodeProvider { + + /** + * Creates and registers this proxy. If no suitable service is available for the proxy, returns + * null. + */ + @Nullable + public static ProxyGeocodeProvider createAndRegister(Context context) { + ProxyGeocodeProvider proxy = new ProxyGeocodeProvider(context); + if (proxy.register()) { + return proxy; + } else { + return null; + } + } + + private final ServiceWatcher mServiceWatcher; + + private ProxyGeocodeProvider(Context context) { + mServiceWatcher = + ServiceWatcher.create( + context, + "GeocoderProxy", + CurrentUserServiceSupplier.createFromConfig( + context, + GeocodeProviderBase.ACTION_GEOCODE_PROVIDER, + com.android.internal.R.bool.config_enableGeocoderOverlay, + com.android.internal.R.string.config_geocoderProviderPackageName), + null); + } + + private boolean register() { + boolean resolves = mServiceWatcher.checkServiceResolves(); + if (resolves) { + mServiceWatcher.register(); + } + return resolves; + } + + /** Reverse geocodes. */ + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + mServiceWatcher.runOnBinder( + new ServiceWatcher.BinderOperation() { + @Override + public void run(IBinder binder) throws RemoteException { + IGeocodeProvider.Stub.asInterface(binder).reverseGeocode(request, callback); + } + + @Override + public void onError(Throwable t) { + try { + callback.onError(t.toString()); + } catch (RemoteException e) { + // ignore + } + } + }); + } + + /** Forward geocodes. */ + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + mServiceWatcher.runOnBinder( + new ServiceWatcher.BinderOperation() { + @Override + public void run(IBinder binder) throws RemoteException { + IGeocodeProvider.Stub.asInterface(binder).forwardGeocode(request, callback); + } + + @Override + public void onError(Throwable t) { + try { + callback.onError(t.toString()); + } catch (RemoteException e) { + // ignore + } + } + }); + } +} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 7fb3e001c4c3..9a76ebd148aa 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -18,6 +18,7 @@ package com.android.server.locksettings; import static android.security.Flags.reportPrimaryAuthAttempts; import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; +import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS; import static android.Manifest.permission.SET_INITIAL_LOCK; @@ -27,6 +28,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE; import static android.content.Context.KEYGUARD_SERVICE; +import static android.content.Intent.ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_SYSTEM; @@ -1201,8 +1203,9 @@ public class LockSettingsService extends ILockSettings.Stub { final boolean inSetupWizard = Settings.Secure.getIntForUser(cr, Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0; - final boolean secureFrp = Settings.Global.getInt(cr, - Settings.Global.SECURE_FRP_MODE, 0) == 1; + final boolean secureFrp = android.security.Flags.frpEnforcement() + ? mStorage.isFactoryResetProtectionActive() + : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1); if (inSetupWizard && secureFrp) { throw new SecurityException("Cannot change credential in SUW while factory reset" @@ -2332,8 +2335,13 @@ public class LockSettingsService extends ILockSettings.Stub { synchronized (mSpManager) { if (isSpecialUserId(userId)) { - return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(), + response = mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(), credential, progressCallback); + if (android.security.Flags.frpEnforcement() && response.isMatched() + && userId == USER_FRP) { + mStorage.deactivateFactoryResetProtectionWithoutSecret(); + } + return response; } long protectorId = getCurrentLskfBasedProtectorId(userId); @@ -3054,6 +3062,7 @@ public class LockSettingsService extends ILockSettings.Stub { setCurrentLskfBasedProtectorId(newProtectorId, userId); LockPatternUtils.invalidateCredentialTypeCache(); synchronizeUnifiedChallengeForProfiles(userId, profilePasswords); + sendMainUserCredentialChangedNotificationIfNeeded(userId); setUserPasswordMetrics(credential, userId); mUnifiedProfilePasswordCache.removePassword(userId); @@ -3071,6 +3080,24 @@ public class LockSettingsService extends ILockSettings.Stub { return newProtectorId; } + private void sendMainUserCredentialChangedNotificationIfNeeded(int userId) { + if (!android.security.Flags.frpEnforcement()) { + return; + } + + if (userId != mInjector.getUserManagerInternal().getMainUserId()) { + return; + } + + sendBroadcast(new Intent(ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED), + UserHandle.of(userId), CONFIGURE_FACTORY_RESET_PROTECTION); + } + + @VisibleForTesting + void sendBroadcast(Intent intent, UserHandle userHandle, String permission) { + mContext.sendBroadcastAsUser(intent, userHandle, permission, /* options */ null); + } + private void removeBiometricsForUser(int userId) { removeAllFingerprintForUser(userId); removeAllFaceForUser(userId); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 6d123ccebc7c..158d444bcff2 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -34,6 +34,7 @@ import android.os.Environment; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.service.persistentdata.PersistentDataBlockManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AtomicFile; @@ -587,6 +588,10 @@ class LockSettingsStorage { return mPersistentDataBlockManagerInternal; } + /** + * Writes main user credential handle to the persistent data block, to enable factory reset + * protection to be deactivated with the credential. + */ public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi, byte[] payload) { PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager(); @@ -610,6 +615,31 @@ class LockSettingsStorage { } } + public void deactivateFactoryResetProtectionWithoutSecret() { + PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager(); + if (persistentDataBlock != null) { + persistentDataBlock.deactivateFactoryResetProtectionWithoutSecret(); + } else { + Slog.wtf(TAG, "Failed to get PersistentDataBlockManagerInternal"); + } + } + + public boolean isFactoryResetProtectionActive() { + PersistentDataBlockManager persistentDataBlockManager = + mContext.getSystemService(PersistentDataBlockManager.class); + if (persistentDataBlockManager != null) { + return persistentDataBlockManager.isFactoryResetProtectionActive(); + } else { + Slog.wtf(TAG, "Failed to get PersistentDataBlockManager"); + // This should never happen, but in the event it does, let's not block the user. This + // may be the wrong call, since if an attacker can find a way to prevent us from + // getting the PersistentDataBlockManager they can defeat FRP, but if they can block + // access to PersistentDataBlockManager they must have compromised the system and we've + // probably already lost this battle. + return false; + } + } + /** * Provides a concrete data structure to represent the minimal information from * a user's LSKF-based SP protector that is needed to verify the user's LSKF, diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 8cb5cef38ec6..f97f6d2350bc 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -105,6 +105,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -151,7 +152,9 @@ public class MediaSessionService extends SystemService implements Monitor { private boolean mHasFeatureLeanback; private ActivityManagerInternal mActivityManagerInternal; private UsageStatsManagerInternal mUsageStatsManagerInternal; - private final Set<Integer> mUserEngagingSessions = new HashSet<>(); + + /* Maps uid with all user engaging session tokens associated to it */ + private final Map<Integer, Set<MediaSession.Token>> mUserEngagingSessions = new HashMap<>(); // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) // It's always not null after the MediaSessionService is started. @@ -615,32 +618,36 @@ public class MediaSessionService extends SystemService implements Monitor { } private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) { - if (!android.app.usage.Flags.userInteractionTypeApi()) { - return; - } + if (!android.app.usage.Flags.userInteractionTypeApi() + || !(record instanceof MediaSessionRecord)) { + return; + } - String packageName = record.getPackageName(); - int sessionUid = record.getUid(); - String actionToLog = null; - if (userEngaged) { - if (!mUserEngagingSessions.contains(sessionUid)) { - actionToLog = "start"; - } - mUserEngagingSessions.add(sessionUid); - } else { - if (mUserEngagingSessions.contains(sessionUid)) { - actionToLog = "stop"; - } + String packageName = record.getPackageName(); + int sessionUid = record.getUid(); + String actionToLog = null; + MediaSession.Token token = ((MediaSessionRecord) record).getSessionToken(); + if (userEngaged) { + if (!mUserEngagingSessions.containsKey(sessionUid)) { + mUserEngagingSessions.put(sessionUid, new HashSet<>()); + actionToLog = "start"; + } + mUserEngagingSessions.get(sessionUid).add(token); + } else if (mUserEngagingSessions.containsKey(sessionUid)) { + mUserEngagingSessions.get(sessionUid).remove(token); + if (mUserEngagingSessions.get(sessionUid).isEmpty()) { + actionToLog = "stop"; mUserEngagingSessions.remove(sessionUid); } + } - if (actionToLog != null) { - PersistableBundle extras = new PersistableBundle(); - extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media"); - extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog); - mUsageStatsManagerInternal.reportUserInteractionEvent( - packageName, record.getUserId(), extras); - } + if (actionToLog != null) { + PersistableBundle extras = new PersistableBundle(); + extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media"); + extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog); + mUsageStatsManagerInternal.reportUserInteractionEvent( + packageName, record.getUserId(), extras); + } } void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index f9ffb1cebac4..b5c51af47009 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -26,6 +26,8 @@ import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.OBSERVE_NETWORK_POLICY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.isProcStateConsideredInteraction; import static android.app.ActivityManager.printCapabilitiesSummary; @@ -85,8 +87,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_ import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP; +import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; +import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; @@ -97,6 +101,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; +import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.allowedReasonsToString; import static android.net.NetworkPolicyManager.blockedReasonsToString; import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground; @@ -469,7 +474,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24; - private static final int UID_MSG_STATE_CHANGED = 100; + @VisibleForTesting + static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; private static final String PROP_SUB_PLAN_OWNER = "persist.sys.sub_plan_owner"; @@ -1075,8 +1081,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; - // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint. - mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes, cutpoint, "android"); mNetworkManager.registerObserver(mAlertObserver); @@ -1185,6 +1189,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } final private IUidObserver mUidObserver = new UidObserver() { + + /** + * Returns whether the uid state change information is relevant for the service. If the + * state information does not lead to any change in the network rules, it can safely be + * ignored. + */ + @GuardedBy("mUidStateCallbackInfos") + private boolean isUidStateChangeRelevant(UidStateCallbackInfo previousInfo, + int newProcState, long newProcStateSeq, int newCapability) { + if (previousInfo.procStateSeq == -1) { + // No previous record. Always process the first state change callback. + return true; + } + if (newProcStateSeq <= previousInfo.procStateSeq) { + // Stale callback. Ignore. + return false; + } + final int previousProcState = previousInfo.procState; + if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE) + != (newProcState >= BACKGROUND_THRESHOLD_STATE)) { + // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the + // BACKGROUND chain may change. + return true; + } + if ((previousProcState <= TOP_THRESHOLD_STATE) + != (newProcState <= TOP_THRESHOLD_STATE)) { + // Proc-state change crossed TOP_THRESHOLD_STATE: Network rules for the + // LOW_POWER_STANDBY chain may change. + return true; + } + if ((previousProcState <= FOREGROUND_THRESHOLD_STATE) + != (newProcState <= FOREGROUND_THRESHOLD_STATE)) { + // Proc-state change crossed FOREGROUND_THRESHOLD_STATE: Network rules for many + // different chains may change. + return true; + } + final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK + | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; + if ((previousInfo.capability & networkCapabilities) + != (newCapability & networkCapabilities)) { + return true; + } + return false; + } + @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, @ProcessCapability int capability) { synchronized (mUidStateCallbackInfos) { @@ -1193,13 +1242,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { callbackInfo = new UidStateCallbackInfo(); mUidStateCallbackInfos.put(uid, callbackInfo); } - if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) { + if (isUidStateChangeRelevant(callbackInfo, procState, procStateSeq, capability)) { callbackInfo.update(uid, procState, procStateSeq, capability); - } - if (!callbackInfo.isPending) { - mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo) - .sendToTarget(); - callbackInfo.isPending = true; + if (!callbackInfo.isPending) { + mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo) + .sendToTarget(); + callbackInfo.isPending = true; + } } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d751186b2869..53ae60b0cc76 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1879,11 +1879,11 @@ public class NotificationManagerService extends SystemService { true, record.getUserId(), REASON_TIMEOUT, null); // If cancellation will be prevented due to lifetime extension, we send an // update to system UI. + final int packageImportance = getPackageImportanceWithIdentity( + record.getSbn().getPackageName()); synchronized (mNotificationLock) { maybeNotifySystemUiListenerLifetimeExtendedLocked(record, - record.getSbn().getPackageName(), - mActivityManager.getPackageImportance( - record.getSbn().getPackageName())); + record.getSbn().getPackageName(), packageImportance); } } else { cancelNotification(record.getSbn().getUid(), @@ -3852,7 +3852,7 @@ public class NotificationManagerService extends SystemService { // If cancellation will be prevented due to lifetime extension, we send an update to // system UI. NotificationRecord record = null; - final int packageImportance = mActivityManager.getPackageImportance(pkg); + final int packageImportance = getPackageImportanceWithIdentity(pkg); synchronized (mNotificationLock) { record = findNotificationLocked(pkg, tag, id, userId); maybeNotifySystemUiListenerLifetimeExtendedLocked(record, pkg, @@ -3877,10 +3877,9 @@ public class NotificationManagerService extends SystemService { pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, userId, REASON_APP_CANCEL_ALL); + final int packageImportance = getPackageImportanceWithIdentity(pkg); // If cancellation will be prevented due to lifetime extension, we send updates // to system UI. - // In this case, we need to hold the lock to access these lists. - final int packageImportance = mActivityManager.getPackageImportance(pkg); synchronized (mNotificationLock) { notifySystemUiListenerLifetimeExtendedListLocked(mNotificationList, packageImportance); @@ -5029,7 +5028,7 @@ public class NotificationManagerService extends SystemService { pkg = info.component.getPackageName(); } if (lifetimeExtensionRefactor()) { - packageImportance = mActivityManager.getPackageImportance(pkg); + packageImportance = getPackageImportanceWithIdentity(pkg); } else { packageImportance = IMPORTANCE_NONE; } @@ -5348,7 +5347,7 @@ public class NotificationManagerService extends SystemService { final int packageImportance; try { if (lifetimeExtensionRefactor()) { - packageImportance = mActivityManager.getPackageImportance(pkg); + packageImportance = getPackageImportanceWithIdentity(pkg); } else { packageImportance = IMPORTANCE_NONE; } @@ -7609,14 +7608,9 @@ public class NotificationManagerService extends SystemService { } } - // Need escalated privileges to get package importance - final long token = Binder.clearCallingIdentity(); - boolean isAppForeground; - try { - isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; - } finally { - Binder.restoreCallingIdentity(token); - } + // Need escalated privileges to get package importance. + final int packageImportance = getPackageImportanceWithIdentity(pkg); + boolean isAppForeground = packageImportance == IMPORTANCE_FOREGROUND; mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, tracker)); return true; } @@ -7944,9 +7938,9 @@ public class NotificationManagerService extends SystemService { NotificationRecord r = mNotificationsByKey.get(key); packageName = r != null ? r.getSbn().getPackageName() : null; } + final int packageImportance = getPackageImportanceWithIdentity(packageName); boolean isAppForeground = packageName != null - && mActivityManager.getPackageImportance(packageName) - == IMPORTANCE_FOREGROUND; + && packageImportance == IMPORTANCE_FOREGROUND; synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { @@ -11867,6 +11861,18 @@ public class NotificationManagerService extends SystemService { } } + @FlaggedApi(FLAG_LIFETIME_EXTENSION_REFACTOR) + private int getPackageImportanceWithIdentity(String pkg) { + final long token = Binder.clearCallingIdentity(); + final int packageImportance; + try { + packageImportance = mActivityManager.getPackageImportance(pkg); + } finally { + Binder.restoreCallingIdentity(token); + } + return packageImportance; + } + public class NotificationListeners extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners"; static final String TAG_REQUESTED_LISTENERS = "request_listeners"; diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index 722654a9102c..53244f9e5ecf 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -50,15 +50,6 @@ flag { } flag { - name: "sensitive_notification_app_protection" - namespace: "systemui" - description: "This flag controls the sensitive notification app protections while screen sharing" - bug: "312784351" - # Referenced in WM where WM starts before DeviceConfig - is_fixed_read_only: true -} - -flag { name: "notification_reduce_messagequeue_usage" namespace: "systemui" description: "When this flag is on, NMS will no longer call removeMessage() and hasCallbacks() on Handler" diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index d987622676b5..872952299055 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -1143,10 +1143,9 @@ public final class OverlayManagerService extends SystemService { }; private static final class PackageManagerHelperImpl implements PackageManagerHelper { - private static final class PackageStateUsers { + private static class PackageStateUsers { private PackageState mPackageState; - private Boolean mDefinesOverlayable = null; - private final ArraySet<Integer> mInstalledUsers = new ArraySet<>(); + private final Set<Integer> mInstalledUsers = new ArraySet<>(); private PackageStateUsers(@NonNull PackageState packageState) { this.mPackageState = packageState; } @@ -1196,11 +1195,13 @@ public final class OverlayManagerService extends SystemService { return userPackages; } - private PackageStateUsers getRawPackageStateForUser(@NonNull final String packageName, + @Override + @Nullable + public PackageState getPackageStateForUser(@NonNull final String packageName, final int userId) { final PackageStateUsers pkg = mCache.get(packageName); if (pkg != null && pkg.mInstalledUsers.contains(userId)) { - return pkg; + return pkg.mPackageState; } try { if (!mPackageManager.isPackageAvailable(packageName, userId)) { @@ -1214,14 +1215,8 @@ public final class OverlayManagerService extends SystemService { return addPackageUser(packageName, userId); } - @Override - public PackageState getPackageStateForUser(@NonNull final String packageName, - final int userId) { - final PackageStateUsers pkg = getRawPackageStateForUser(packageName, userId); - return pkg != null ? pkg.mPackageState : null; - } - - private PackageStateUsers addPackageUser(@NonNull final String packageName, + @NonNull + private PackageState addPackageUser(@NonNull final String packageName, final int user) { final PackageState pkg = mPackageManagerInternal.getPackageStateInternal(packageName); if (pkg == null) { @@ -1233,20 +1228,20 @@ public final class OverlayManagerService extends SystemService { } @NonNull - private PackageStateUsers addPackageUser(@NonNull final PackageState pkg, + private PackageState addPackageUser(@NonNull final PackageState pkg, final int user) { PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName()); if (pkgUsers == null) { pkgUsers = new PackageStateUsers(pkg); mCache.put(pkg.getPackageName(), pkgUsers); - } else if (pkgUsers.mPackageState != pkg) { + } else { pkgUsers.mPackageState = pkg; - pkgUsers.mDefinesOverlayable = null; } pkgUsers.mInstalledUsers.add(user); - return pkgUsers; + return pkgUsers.mPackageState; } + @NonNull private void removePackageUser(@NonNull final String packageName, final int user) { final PackageStateUsers pkgUsers = mCache.get(packageName); @@ -1264,15 +1259,15 @@ public final class OverlayManagerService extends SystemService { } } + @Nullable public PackageState onPackageAdded(@NonNull final String packageName, final int userId) { - final var pu = addPackageUser(packageName, userId); - return pu != null ? pu.mPackageState : null; + return addPackageUser(packageName, userId); } + @Nullable public PackageState onPackageUpdated(@NonNull final String packageName, final int userId) { - final var pu = addPackageUser(packageName, userId); - return pu != null ? pu.mPackageState : null; + return addPackageUser(packageName, userId); } public void onPackageRemoved(@NonNull final String packageName, final int userId) { @@ -1312,30 +1307,22 @@ public final class OverlayManagerService extends SystemService { return (pkgs.length == 0) ? null : pkgs[0]; } + @Nullable @Override public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, @NonNull String targetOverlayableName, int userId) throws IOException { - final var psu = getRawPackageStateForUser(packageName, userId); - final var pkg = (psu == null || psu.mPackageState == null) - ? null : psu.mPackageState.getAndroidPackage(); + var packageState = getPackageStateForUser(packageName, userId); + var pkg = packageState == null ? null : packageState.getAndroidPackage(); if (pkg == null) { throw new IOException("Unable to get target package"); } - if (Boolean.FALSE.equals(psu.mDefinesOverlayable)) { - return null; - } - ApkAssets apkAssets = null; try { apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath(), ApkAssets.PROPERTY_ONLY_OVERLAYABLES); - if (psu.mDefinesOverlayable == null) { - psu.mDefinesOverlayable = apkAssets.definesOverlayable(); - } - return Boolean.FALSE.equals(psu.mDefinesOverlayable) - ? null : apkAssets.getOverlayableInfo(targetOverlayableName); + return apkAssets.getOverlayableInfo(targetOverlayableName); } finally { if (apkAssets != null) { try { @@ -1349,29 +1336,24 @@ public final class OverlayManagerService extends SystemService { @Override public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) throws IOException { - final var psu = getRawPackageStateForUser(targetPackageName, userId); - var pkg = (psu == null || psu.mPackageState == null) - ? null : psu.mPackageState.getAndroidPackage(); + var packageState = getPackageStateForUser(targetPackageName, userId); + var pkg = packageState == null ? null : packageState.getAndroidPackage(); if (pkg == null) { throw new IOException("Unable to get target package"); } - if (psu.mDefinesOverlayable == null) { - ApkAssets apkAssets = null; - try { - apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath(), - ApkAssets.PROPERTY_ONLY_OVERLAYABLES); - psu.mDefinesOverlayable = apkAssets.definesOverlayable(); - } finally { - if (apkAssets != null) { - try { - apkAssets.close(); - } catch (Throwable ignored) { - } + ApkAssets apkAssets = null; + try { + apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath()); + return apkAssets.definesOverlayable(); + } finally { + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { } } } - return psu.mDefinesOverlayable; } @Override diff --git a/services/core/java/com/android/server/ondeviceintelligence/OWNERS b/services/core/java/com/android/server/ondeviceintelligence/OWNERS new file mode 100644 index 000000000000..09774f78d712 --- /dev/null +++ b/services/core/java/com/android/server/ondeviceintelligence/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/app/ondeviceintelligence/OWNERS diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 295528e14ca7..f9d81127fd86 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1625,10 +1625,11 @@ public class LauncherAppsService extends SystemService { } @Override + @NonNull public List<String> getPreInstalledSystemPackages(UserHandle user) { if (!canAccessProfile(user.getIdentifier(), "Can't access preinstalled packages for another user")) { - return null; + return new ArrayList<>(); } final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index 85d2df320fc9..bbce26c85549 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -27,11 +27,14 @@ import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.Flags; import android.content.pm.PackageManager; import android.os.Build; import android.os.Environment; import android.os.FileUtils; +import android.os.SystemProperties; import android.os.Trace; import android.text.TextUtils; import android.util.ArraySet; @@ -50,9 +53,15 @@ import libcore.io.IoUtils; import java.io.File; import java.io.IOException; +import java.util.Set; final class PackageAbiHelperImpl implements PackageAbiHelper { + @Nullable + private static String[] sNativelySupported32BitAbis = null; + @Nullable + private static String[] sNativelySupported64BitAbis = null; + private static String calculateBundledApkRoot(final String codePathString) { final File codePath = new File(codePathString); final File codeRoot; @@ -122,13 +131,20 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { } } - private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws - PackageManagerException { + private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet, + boolean forceMatch) throws PackageManagerException { if (copyRet < 0) { if (copyRet != PackageManager.NO_NATIVE_LIBRARIES && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { throw new PackageManagerException(copyRet, message); } + + if (forceMatch && copyRet == PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS, + "The multiArch app's native libs don't support all the natively" + + " supported ABIs of the device."); + } } } @@ -296,7 +312,40 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { return new Abis(primaryCpuAbi, secondaryCpuAbi); } + @NonNull + private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) { + Set<String> nativelySupportedAbis = new ArraySet<>(); + for (int i = 0; i < supportedAbis.length; i++) { + final String currentAbi = supportedAbis[i]; + // In presence of a native bridge this means the Abi is emulated. + final String currentIsa = VMRuntime.getInstructionSet(currentAbi); + if (TextUtils.isEmpty(SystemProperties.get("ro.dalvik.vm.isa." + currentIsa))) { + nativelySupportedAbis.add(currentAbi); + } + } + return nativelySupportedAbis.toArray(new String[0]); + } + + private static String[] getNativelySupported32BitAbis() { + if (sNativelySupported32BitAbis != null) { + return sNativelySupported32BitAbis; + } + + sNativelySupported32BitAbis = getNativelySupportedAbis(Build.SUPPORTED_32_BIT_ABIS); + return sNativelySupported32BitAbis; + } + + private static String[] getNativelySupported64BitAbis() { + if (sNativelySupported64BitAbis != null) { + return sNativelySupported64BitAbis; + } + + sNativelySupported64BitAbis = getNativelySupportedAbis(Build.SUPPORTED_64_BIT_ABIS); + return sNativelySupported64BitAbis; + } + @Override + @SuppressWarnings("AndroidFrameworkCompatChange") // the check is before the apk is installed public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp, boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException { @@ -334,18 +383,33 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { primaryCpuAbi = null; secondaryCpuAbi = null; if (pkg.isMultiArch()) { + // Force the match for these cases + // 1. pkg.getTargetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM + // 2. cpuAbiOverride is null. If it is non-null, it is set via shell for testing + final boolean forceMatch = Flags.forceMultiArchNativeLibsMatch() + && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM + && cpuAbiOverride == null; + + String[] supported32BitAbis = forceMatch ? getNativelySupported32BitAbis() + : Build.SUPPORTED_32_BIT_ABIS; + String[] supported64BitAbis = forceMatch ? getNativelySupported64BitAbis() + : Build.SUPPORTED_64_BIT_ABIS; + + final boolean systemSupports32BitAbi = supported32BitAbis.length > 0; + final boolean systemSupports64BitAbi = supported64BitAbis.length > 0; + int abi32 = PackageManager.NO_NATIVE_LIBRARIES; int abi64 = PackageManager.NO_NATIVE_LIBRARIES; - if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (systemSupports32BitAbi) { if (extractLibs) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, + nativeLibraryRoot, supported32BitAbis, useIsaSpecificSubdirs, onIncremental); } else { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); abi32 = NativeLibraryHelper.findSupportedAbi( - handle, Build.SUPPORTED_32_BIT_ABIS); + handle, supported32BitAbis); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -357,24 +421,26 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { } maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 32 bit native libs for multiarch app.", abi32); + "Error unpackaging 32 bit native libs for multiarch app.", abi32, + forceMatch && systemSupports32BitAbi); - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (systemSupports64BitAbi) { if (extractLibs) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, + nativeLibraryRoot, supported64BitAbis, useIsaSpecificSubdirs, onIncremental); } else { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); abi64 = NativeLibraryHelper.findSupportedAbi( - handle, Build.SUPPORTED_64_BIT_ABIS); + handle, supported64BitAbis); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 64 bit native libs for multiarch app.", abi64); + "Error unpackaging 64 bit native libs for multiarch app.", abi64, + forceMatch && systemSupports64BitAbi); if (abi64 >= 0) { // Shared library native libs should be in the APK zip aligned @@ -382,11 +448,11 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Shared library native lib extraction not supported"); } - primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; + primaryCpuAbi = supported64BitAbis[abi64]; } if (abi32 >= 0) { - final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; + final String abi = supported32BitAbis[abi32]; if (abi64 >= 0) { if (pkg.is32BitAbiPreferred()) { secondaryCpuAbi = primaryCpuAbi; diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index d644235b8714..449e9ab8c5ec 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -240,7 +240,7 @@ class ShortcutPackage extends ShortcutPackageItem { @Override protected boolean canRestoreAnyVersion() { - return false; + return true; } @Override diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index a9d2858fd36e..c2f74a8895cb 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -232,7 +232,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_UNMUTE_MICROPHONE, UserManager.DISALLOW_UNMUTE_DEVICE, UserManager.DISALLOW_CAMERA, - UserManager.DISALLOW_ASSIST_CONTENT + UserManager.DISALLOW_ASSIST_CONTENT, + UserManager.DISALLOW_CONFIG_DEFAULT_APPS ); /** @@ -288,7 +289,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_SMS, UserManager.DISALLOW_USB_FILE_TRANSFER, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, - UserManager.DISALLOW_UNMUTE_MICROPHONE + UserManager.DISALLOW_UNMUTE_MICROPHONE, + UserManager.DISALLOW_CONFIG_DEFAULT_APPS ); /** diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index f44fcf002a62..21e2bf2e76e5 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -42,6 +42,7 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AppOpsManager.AttributionFlags; import android.app.IActivityManager; +import android.companion.virtual.VirtualDeviceManager; import android.content.AttributionSource; import android.content.AttributionSourceState; import android.content.Context; @@ -78,6 +79,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.TriFunction; import com.android.server.LocalServices; +import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider; import com.android.server.pm.pkg.AndroidPackage; @@ -135,6 +137,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Nullable private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider; + @Nullable + private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal; + PermissionManagerService(@NonNull Context context, @NonNull ArrayMap<String, FeatureInfo> availableFeatures) { // The package info cache is the cache for package and permission information. @@ -146,6 +151,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { mContext = context; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mAppOpsManager = context.getSystemService(AppOpsManager.class); + mVirtualDeviceManagerInternal = + LocalServices.getService(VirtualDeviceManagerInternal.class); mAttributionSourceRegistry = new AttributionSourceRegistry(context); @@ -246,16 +253,30 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PackageManager.PERMISSION_DENIED; } + String persistentDeviceId = getPersistentDeviceId(deviceId); + final CheckPermissionDelegate checkPermissionDelegate; synchronized (mLock) { checkPermissionDelegate = mCheckPermissionDelegate; } - - if (checkPermissionDelegate == null) { - return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId); + if (checkPermissionDelegate == null) { + return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, + persistentDeviceId); } return checkPermissionDelegate.checkUidPermission(uid, permissionName, - deviceId, mPermissionManagerServiceImpl::checkUidPermission); + persistentDeviceId, mPermissionManagerServiceImpl::checkUidPermission); + } + + private String getPersistentDeviceId(int deviceId) { + if (deviceId == Context.DEVICE_ID_DEFAULT) { + return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; + } + + if (mVirtualDeviceManagerInternal == null) { + mVirtualDeviceManagerInternal = + LocalServices.getService(VirtualDeviceManagerInternal.class); + } + return mVirtualDeviceManagerInternal.getPersistentIdForDevice(deviceId); } @Override @@ -608,15 +629,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, int deviceId, int userId) { + String persistentDeviceId = getPersistentDeviceId(deviceId); return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName, - permissionName, deviceId, userId); + permissionName, persistentDeviceId, userId); } @Override public boolean isPermissionRevokedByPolicy(String packageName, String permissionName, int deviceId, int userId) { + String persistentDeviceId = getPersistentDeviceId(deviceId); return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName, - permissionName, deviceId, userId); + permissionName, persistentDeviceId, userId); } @Override @@ -914,13 +937,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @param uid the UID to be checked * @param permissionName the name of the permission to be checked - * @param deviceId The device ID + * @param persistentDeviceId The persistent device ID * @param superImpl the original implementation that can be delegated to * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise */ - int checkUidPermission(int uid, @NonNull String permissionName, int deviceId, - TriFunction<Integer, String, Integer, Integer> superImpl); + int checkUidPermission(int uid, @NonNull String permissionName, String persistentDeviceId, + TriFunction<Integer, String, String, Integer> superImpl); /** * @return list of delegated permissions @@ -965,17 +988,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId, - @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) { + public int checkUidPermission(int uid, @NonNull String permissionName, + String persistentDeviceId, + @NonNull TriFunction<Integer, String, String, Integer> superImpl) { if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) { final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(Process.SHELL_UID, permissionName, deviceId); + return superImpl.apply(Process.SHELL_UID, permissionName, persistentDeviceId); } finally { Binder.restoreCallingIdentity(identity); } } - return superImpl.apply(uid, permissionName, deviceId); + return superImpl.apply(uid, permissionName, persistentDeviceId); } @Override 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 c5b637d8b48b..70913c351eeb 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -682,7 +682,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + public int getPermissionFlags(String packageName, String permName, String deviceId, int userId) { final int callingUid = Binder.getCallingUid(); return getPermissionFlagsInternal(packageName, permName, callingUid, userId); @@ -726,8 +726,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, - int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) { final int callingUid = Binder.getCallingUid(); boolean overridePolicy = false; @@ -917,8 +916,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public int checkPermission(String pkgName, String permName, String persistentDeviceId, - int userId) { + public int checkPermission(String pkgName, String permName, String deviceId, int userId) { if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; } @@ -985,11 +983,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } private int checkUidPermission(int uid, String permName) { - return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT); + return checkUidPermission(uid, permName, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT); } @Override - public int checkUidPermission(int uid, String permName, int deviceId) { + public int checkUidPermission(int uid, String permName, String deviceId) { final int userId = UserHandle.getUserId(uid); if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; @@ -1001,7 +999,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt @Override public Map<String, PermissionManager.PermissionState> getAllPermissionStates( - @NonNull String packageName, @NonNull String persistentDeviceId, int userId) { + @NonNull String packageName, @NonNull String deviceId, int userId) { throw new UnsupportedOperationException( "This method is supported in newer implementation only"); } @@ -1315,8 +1313,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public void grantRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId) { + public void grantRuntimePermission(String packageName, String permName, String deviceId, + int userId) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) @@ -1489,12 +1487,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public void revokeRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, String deviceId, + int userId, String reason) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, - Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED; + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) + == PackageManager.PERMISSION_GRANTED; revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId, reason, mDefaultPermissionCallback); @@ -1880,7 +1879,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permName, - int deviceId, @UserIdInt int userId) { + String deviceId, @UserIdInt int userId) { final int callingUid = Binder.getCallingUid(); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( @@ -1943,7 +1942,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId, + public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java index 7c1042535973..47032ea2d6af 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -141,11 +141,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param packageName the package name for which to get the flags * @param permName the permission for which to get the flags - * @param persistentDeviceId The device for which to get the flags + * @param deviceId The device for which to get the flags * @param userId the user for which to get permission flags * @return the permission flags */ - int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + int getPermissionFlags(String packageName, String permName, String deviceId, @UserIdInt int userId); /** @@ -156,11 +156,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * @param permName The permission for which to update the flags * @param flagMask The flags which to replace * @param flagValues The flags with which to replace - * @param persistentDeviceId The device for which to update the permission flags + * @param deviceId The device for which to update the permission flags * @param userId The user for which to update the permission flags */ void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues, - boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + boolean checkAdjustPolicyFlagPermission, String deviceId, @UserIdInt int userId); /** @@ -295,12 +295,12 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param packageName the package to which to grant the permission * @param permName the permission name to grant - * @param persistentDeviceId the device for which to grant the permission + * @param deviceId the device for which to grant the permission * @param userId the user for which to grant the permission * * @see #revokeRuntimePermission(String, String, String, int, String) */ - void grantRuntimePermission(String packageName, String permName, String persistentDeviceId, + void grantRuntimePermission(String packageName, String permName, String deviceId, @UserIdInt int userId); /** @@ -316,13 +316,13 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param packageName the package from which to revoke the permission * @param permName the permission name to revoke - * @param persistentDeviceId the device for which to revoke the permission + * @param deviceId the device for which to revoke the permission * @param userId the user for which to revoke the permission * @param reason the reason for the revoke, or {@code null} for unspecified * * @see #grantRuntimePermission(String, String, String, int) */ - void revokeRuntimePermission(String packageName, String permName, String persistentDeviceId, + void revokeRuntimePermission(String packageName, String permName, String deviceId, @UserIdInt int userId, String reason); /** @@ -347,7 +347,7 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * @return whether you can show permission rationale UI */ boolean shouldShowRequestPermissionRationale(String packageName, String permName, - int deviceId, @UserIdInt int userId); + String deviceId, @UserIdInt int userId); /** * Checks whether a particular permission has been revoked for a package by policy. Typically, @@ -361,8 +361,8 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * @param userId the device for which you are checking the permission * @return whether the permission is restricted by policy */ - boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId, - @UserIdInt int userId); + boolean isPermissionRevokedByPolicy(String packageName, String permName, + String deviceId, @UserIdInt int userId); /** * Get set of permissions that have been split into more granular or dependent permissions. @@ -389,11 +389,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param pkgName package name * @param permName permission name - * @param persistentDeviceId persistent device ID + * @param deviceId persistent device ID * @param userId user ID * @return permission result {@link PackageManager.PermissionResult} */ - int checkPermission(String pkgName, String permName, String persistentDeviceId, + int checkPermission(String pkgName, String permName, String deviceId, @UserIdInt int userId); /** @@ -401,23 +401,23 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param uid UID * @param permName permission name - * @param deviceId device ID + * @param deviceId persistent device ID * @return permission result {@link PackageManager.PermissionResult} */ - int checkUidPermission(int uid, String permName, int deviceId); + int checkUidPermission(int uid, String permName, String deviceId); /** * Gets the permission states for requested package, persistent device and user. * * @param packageName name of the package you are checking against - * @param persistentDeviceId id of the persistent device you are checking against + * @param deviceId id of the persistent device you are checking against * @param userId id of the user for which to get permission flags * @return mapping of all permission states keyed by their permission names * * @hide */ Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName, - @NonNull String persistentDeviceId, @UserIdInt int userId); + @NonNull String deviceId, @UserIdInt int userId); /** * Get all the package names requesting app op permissions. diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java index 91a778d49d61..c18f856594ed 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java @@ -121,24 +121,22 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + public int getPermissionFlags(String packageName, String permName, String deviceId, int userId) { Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = " - + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId - + ")"); - return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId); + + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")"); + return mService.getPermissionFlags(packageName, permName, deviceId, userId); } @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, - int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) { Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = " + permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues + ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission - + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")"); + + ", deviceId = " + deviceId + ", userId = " + userId + ")"); mService.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, persistentDeviceId, userId); + checkAdjustPolicyFlagPermission, deviceId, userId); } @Override @@ -186,21 +184,20 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public void grantRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId) { + public void grantRuntimePermission(String packageName, String permName, String deviceId, + int userId) { Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = " - + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId - + ")"); - mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId); + + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")"); + mService.grantRuntimePermission(packageName, permName, deviceId, userId); } @Override - public void revokeRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, String deviceId, + int userId, String reason) { Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = " - + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId - + ", reason = " + reason + ")"); - mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId, reason); + + permName + ", deviceId = " + deviceId + ", userId = " + userId + ", reason = " + + reason + ")"); + mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); } @Override @@ -212,16 +209,16 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permName, - int deviceId, int userId) { + String deviceId, int userId) { Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName - + ", permName = " + permName + ", deviceId = " + deviceId - + ", userId = " + userId + ")"); + + ", permName = " + permName + ", deviceId = " + deviceId + ", userId = " + + userId + ")"); return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId, userId); } @Override - public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId, + public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId, int userId) { Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = " + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")"); @@ -235,26 +232,27 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public int checkPermission(String pkgName, String permName, String persistentDeviceId, + public int checkPermission(String pkgName, String permName, String deviceId, int userId) { Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName - + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")"); - return mService.checkPermission(pkgName, permName, persistentDeviceId, userId); + + ", deviceId = " + deviceId + ", userId = " + userId + ")"); + return mService.checkPermission(pkgName, permName, deviceId, userId); } @Override - public int checkUidPermission(int uid, String permName, int deviceId) { - Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " - + permName + ", deviceId = " + deviceId + ")"); + public int checkUidPermission(int uid, String permName, String deviceId) { + Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + + ", deviceId = " + deviceId + ")"); return mService.checkUidPermission(uid, permName, deviceId); } @Override public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName, - @NonNull String persistentDeviceId, int userId) { - Log.i(LOG_TAG, "getAllPermissionStates(packageName = " + packageName - + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")"); - return mService.getAllPermissionStates(packageName, persistentDeviceId, userId); + @NonNull String deviceId, int userId) { + Log.i(LOG_TAG, + "getAllPermissionStates(packageName = " + packageName + ", deviceId = " + deviceId + + ", userId = " + userId + ")"); + return mService.getAllPermissionStates(packageName, deviceId, userId); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java index 0a4ff0797c97..40139baf0e98 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java @@ -154,12 +154,10 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + public int getPermissionFlags(String packageName, String permName, String deviceId, @UserIdInt int userId) { - int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, - persistentDeviceId, userId); - int newVal = mNewImplementation.getPermissionFlags(packageName, permName, - persistentDeviceId, userId); + int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId); + int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId); if (!Objects.equals(oldVal, newVal)) { signalImplDifference("getPermissionFlags"); @@ -169,12 +167,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, @UserIdInt int userId) { mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, persistentDeviceId, userId); + checkAdjustPolicyFlagPermission, deviceId, userId); mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, persistentDeviceId, userId); + checkAdjustPolicyFlagPermission, deviceId, userId); } @Override @@ -239,21 +237,17 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public void grantRuntimePermission(String packageName, String permName, - String persistentDeviceId, @UserIdInt int userId) { - mOldImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId, - userId); - mNewImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId, - userId); + public void grantRuntimePermission(String packageName, String permName, String deviceId, + @UserIdInt int userId) { + mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId); + mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId); } @Override - public void revokeRuntimePermission(String packageName, String permName, - String persistentDeviceId, @UserIdInt int userId, String reason) { - mOldImplementation.revokeRuntimePermission(packageName, permName, persistentDeviceId, - userId, reason); - mNewImplementation.revokeRuntimePermission(packageName, permName, persistentDeviceId, - userId, reason); + public void revokeRuntimePermission(String packageName, String permName, String deviceId, + @UserIdInt int userId, String reason) { + mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); + mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); } @Override @@ -265,7 +259,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permName, - int deviceId, @UserIdInt int userId) { + String deviceId, @UserIdInt int userId) { boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName, permName, deviceId, userId); boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName, @@ -278,7 +272,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId, + public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId, @UserIdInt int userId) { boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId); @@ -303,12 +297,9 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public int checkPermission(String pkgName, String permName, String persistentDeviceId, - int userId) { - int oldVal = mOldImplementation.checkPermission(pkgName, permName, persistentDeviceId, - userId); - int newVal = mNewImplementation.checkPermission(pkgName, permName, persistentDeviceId, - userId); + public int checkPermission(String pkgName, String permName, String deviceId, int userId) { + int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId); + int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId); if (!Objects.equals(oldVal, newVal)) { signalImplDifference("checkPermission"); @@ -317,7 +308,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public int checkUidPermission(int uid, String permName, int deviceId) { + public int checkUidPermission(int uid, String permName, String deviceId) { int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId); int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId); @@ -329,8 +320,8 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer @Override public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName, - @NonNull String persistentDeviceId, int userId) { - return mNewImplementation.getAllPermissionStates(packageName, persistentDeviceId, userId); + @NonNull String deviceId, int userId) { + return mNewImplementation.getAllPermissionStates(packageName, deviceId, userId); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java index bc29e6773bca..981d3d92b15a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java @@ -159,11 +159,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + public int getPermissionFlags(String packageName, String permName, String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags"); try { - return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId); + return mService.getPermissionFlags(packageName, permName, deviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } @@ -171,13 +171,12 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, - int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags"); try { mService.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, persistentDeviceId, userId); + checkAdjustPolicyFlagPermission, deviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } @@ -256,25 +255,24 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public void grantRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId) { + public void grantRuntimePermission(String packageName, String permName, String deviceId, + int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission"); try { - mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId); + mService.grantRuntimePermission(packageName, permName, deviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } } @Override - public void revokeRuntimePermission(String packageName, String permName, - String persistentDeviceId, int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, String deviceId, + int userId, String reason) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission"); try { - mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId, - reason); + mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); } finally { Trace.traceEnd(TRACE_TAG); } @@ -293,19 +291,19 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permName, - int deviceId, int userId) { + String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale"); try { - return mService.shouldShowRequestPermissionRationale( - packageName, permName, deviceId, userId); + return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId, + userId); } finally { Trace.traceEnd(TRACE_TAG); } } @Override - public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId, + public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy"); @@ -328,18 +326,17 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public int checkPermission(String pkgName, String permName, String persistentDeviceId, - int userId) { + public int checkPermission(String pkgName, String permName, String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission"); try { - return mService.checkPermission(pkgName, permName, persistentDeviceId, userId); + return mService.checkPermission(pkgName, permName, deviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } } @Override - public int checkUidPermission(int uid, String permName, int deviceId) { + public int checkUidPermission(int uid, String permName, String deviceId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission"); try { return mService.checkUidPermission(uid, permName, deviceId); @@ -350,11 +347,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag @Override public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName, - @NonNull String persistentDeviceId, int userId) { + @NonNull String deviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl" + "#getAllPermissionStates"); try { - return mService.getAllPermissionStates(packageName, persistentDeviceId, userId); + return mService.getAllPermissionStates(packageName, deviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } diff --git a/services/core/java/com/android/server/selinux/OWNERS b/services/core/java/com/android/server/selinux/OWNERS index 6ca4da2fd740..4cf2066018b5 100644 --- a/services/core/java/com/android/server/selinux/OWNERS +++ b/services/core/java/com/android/server/selinux/OWNERS @@ -1,3 +1,4 @@ # Bug component: 1117393 sandrom@google.com +melisacz@google.com diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java index 0219645bee38..03822aaf76b2 100644 --- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java +++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java @@ -88,7 +88,7 @@ class SelinuxAuditLogsCollector { if (eventTime.isAfter(latestTimestamp)) { latestTimestamp = eventTime; } - if (eventTime.isBefore(mLastWrite)) { + if (eventTime.compareTo(mLastWrite) <= 0) { continue; } Object eventData = event.getData(); diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index 59766ec7a175..f8c678aa3aa3 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -45,6 +45,11 @@ import static android.hardware.SensorPrivacyManager.Sources.OTHER; import static android.hardware.SensorPrivacyManager.Sources.QS_TILE; import static android.hardware.SensorPrivacyManager.Sources.SETTINGS; import static android.hardware.SensorPrivacyManager.Sources.SHELL; +import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_APPS; +import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS; +import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS; +import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED; +import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED; import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE; import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE; import static android.os.UserHandle.USER_NULL; @@ -52,6 +57,9 @@ import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION; import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS; +import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS; +import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS; import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF; import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON; import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA; @@ -63,8 +71,11 @@ import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_ import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.write; +import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -87,6 +98,7 @@ import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.drawable.Icon; +import android.hardware.CameraPrivacyAllowlistEntry; import android.hardware.ISensorPrivacyListener; import android.hardware.ISensorPrivacyManager; import android.hardware.SensorPrivacyManager; @@ -123,6 +135,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.camera.flags.Flags; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; @@ -131,6 +144,7 @@ import com.android.internal.util.dump.DualDumpOutputStream; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; +import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; @@ -139,6 +153,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; @@ -154,7 +169,24 @@ public final class SensorPrivacyService extends SystemService { SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy"; public static final int REMINDER_DIALOG_DELAY_MILLIS = 500; - + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS; + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS; + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS; + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__TOGGLE_ON = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON; + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__TOGGLE_OFF = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF; + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private static final int ACTION__ACTION_UNKNOWN = + PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN; private final Context mContext; private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl; private final UserManagerInternal mUserManagerInternal; @@ -176,6 +208,9 @@ public final class SensorPrivacyService extends SystemService { private CallStateHelper mCallStateHelper; private KeyguardManager mKeyguardManager; + List<CameraPrivacyAllowlistEntry> mCameraPrivacyAllowlist = + new ArrayList<CameraPrivacyAllowlistEntry>(); + private int mCurrentUser = USER_NULL; public SensorPrivacyService(Context context) { @@ -192,6 +227,15 @@ public final class SensorPrivacyService extends SystemService { mPackageManagerInternal = getLocalService(PackageManagerInternal.class); mNotificationManager = mContext.getSystemService(NotificationManager.class); mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(); + ArrayMap<String, Boolean> cameraPrivacyAllowlist = + SystemConfig.getInstance().getCameraPrivacyAllowlist(); + + for (Map.Entry<String, Boolean> entry : cameraPrivacyAllowlist.entrySet()) { + CameraPrivacyAllowlistEntry ent = new CameraPrivacyAllowlistEntry(); + ent.packageName = entry.getKey(); + ent.isMandatory = entry.getValue(); + mCameraPrivacyAllowlist.add(ent); + } } @Override @@ -324,8 +368,15 @@ public final class SensorPrivacyService extends SystemService { mHandler, mHandler::handleSensorPrivacyChanged); mSensorPrivacyStateController.setSensorPrivacyListener( mHandler, - (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged( - userId, toggleType, sensor, state.isEnabled())); + (toggleType, userId, sensor, state) -> { + mHandler.handleSensorPrivacyChanged( + userId, toggleType, sensor, state.isEnabled()); + if (Flags.cameraPrivacyAllowlist()) { + mHandler.handleSensorPrivacyChanged( + userId, toggleType, sensor, state.getState()); + } + }); + } // If sensor privacy is enabled for a sensor, but the device doesn't support sensor privacy @@ -400,9 +451,15 @@ public final class SensorPrivacyService extends SystemService { * @param packageName The package name of the app using the sensor * @param sensor The sensor that is attempting to be used */ + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) private void onSensorUseStarted(int uid, String packageName, int sensor) { UserHandle user = UserHandle.of(mCurrentUser); - if (!isCombinedToggleSensorPrivacyEnabled(sensor)) { + + if (Flags.cameraPrivacyAllowlist() && (sensor == CAMERA) && isAutomotive(mContext)) { + if (!isCameraPrivacyEnabled(packageName)) { + return; + } + } else if (!isCombinedToggleSensorPrivacyEnabled(sensor)) { return; } @@ -727,6 +784,12 @@ public final class SensorPrivacyService extends SystemService { == Configuration.UI_MODE_TYPE_TELEVISION; } + private boolean isAutomotive(Context context) { + int uiMode = context.getResources().getConfiguration().uiMode; + return (uiMode & Configuration.UI_MODE_TYPE_MASK) + == Configuration.UI_MODE_TYPE_CAR; + } + /** * Sets the sensor privacy to the provided state and notifies all listeners of the new * state. @@ -766,6 +829,225 @@ public final class SensorPrivacyService extends SystemService { setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable); } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) + public void setToggleSensorPrivacyState(int userId, int source, int sensor, int state) { + if (DEBUG) { + Log.d(TAG, "callingUid=" + Binder.getCallingUid() + + " callingPid=" + Binder.getCallingPid() + + " setToggleSensorPrivacyState(" + + "userId=" + userId + + " source=" + source + + " sensor=" + sensor + + " state=" + state + + ")"); + } + enforceManageSensorPrivacyPermission(); + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUser; + } + + if (!canChangeToggleSensorPrivacy(userId, sensor)) { + return; + } + if (!supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)) { + // Do not enable sensor privacy if the device doesn't support it. + return; + } + + setToggleSensorPrivacyStateUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, + state); + } + + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private void setToggleSensorPrivacyStateUnchecked(int toggleType, int userId, int source, + int sensor, int state) { + if (DEBUG) { + Log.d(TAG, "callingUid=" + Binder.getCallingUid() + + " callingPid=" + Binder.getCallingPid() + + " setToggleSensorPrivacyStateUnchecked(" + + "userId=" + userId + + " source=" + source + + " sensor=" + sensor + + " state=" + state + + ")"); + } + long[] lastChange = new long[1]; + mSensorPrivacyStateController.atomic(() -> { + SensorState sensorState = mSensorPrivacyStateController + .getState(toggleType, userId, sensor); + lastChange[0] = sensorState.getLastChange(); + mSensorPrivacyStateController.setState( + toggleType, userId, sensor, state, mHandler, + changeSuccessful -> { + if (changeSuccessful) { + if (userId == mUserManagerInternal.getProfileParentId(userId)) { + mHandler.sendMessage(PooledLambda.obtainMessage( + SensorPrivacyServiceImpl::logSensorPrivacyStateToggle, + this, + source, sensor, state, lastChange[0], false)); + } + } + }); + }); + } + + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + private void logSensorPrivacyStateToggle(int source, int sensor, int state, + long lastChange, boolean onShutDown) { + long logMins = Math.max(0, (getCurrentTimeMillis() - lastChange) / (1000 * 60)); + + int logAction = ACTION__ACTION_UNKNOWN; + if (!onShutDown) { + switch(state) { + case ENABLED : + logAction = ACTION__TOGGLE_OFF; + break; + case DISABLED : + logAction = ACTION__TOGGLE_ON; + break; + case AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS : + logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS; + break; + case AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS : + logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS; + break; + case AUTOMOTIVE_DRIVER_ASSISTANCE_APPS : + logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS; + break; + default : + logAction = ACTION__ACTION_UNKNOWN; + break; + } + } + + int logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN; + switch(sensor) { + case CAMERA: + logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA; + break; + case MICROPHONE: + logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__MICROPHONE; + break; + default: + logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN; + break; + } + + int logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN; + switch(source) { + case QS_TILE : + logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__QS_TILE; + break; + case DIALOG : + logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__DIALOG; + break; + case SETTINGS: + logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SETTINGS; + break; + default: + logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN; + break; + } + + if (DEBUG || DEBUG_LOGGING) { + Log.d(TAG, "Logging sensor toggle interaction:" + " logSensor=" + logSensor + + " logAction=" + logAction + " logSource=" + logSource + " logMins=" + + logMins); + } + write(PRIVACY_SENSOR_TOGGLE_INTERACTION, logSensor, logAction, logSource, logMins); + + } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) + public void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, + int state) { + enforceManageSensorPrivacyPermission(); + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUser; + } + int parentId = mUserManagerInternal.getProfileParentId(userId); + forAllUsers(userId2 -> { + if (parentId == mUserManagerInternal.getProfileParentId(userId2)) { + setToggleSensorPrivacyState(userId2, source, sensor, state); + } + }); + } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist() { + enforceObserveSensorPrivacyPermission(); + return mCameraPrivacyAllowlist; + } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public boolean isCameraPrivacyEnabled(String packageName) { + if (DEBUG) { + Log.d(TAG, "callingUid=" + Binder.getCallingUid() + + " callingPid=" + Binder.getCallingPid() + + " isCameraPrivacyEnabled(" + + "packageName=" + packageName + + ")"); + } + enforceObserveSensorPrivacyPermission(); + + int state = mSensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, mCurrentUser, + CAMERA).getState(); + if (state == ENABLED) { + return true; + } else if (state == DISABLED) { + return false; + } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS) { + for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) { + if ((packageName.equals(entry.packageName)) && !entry.isMandatory) { + return false; + } + } + return true; + } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS) { + for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) { + if ((packageName.equals(entry.packageName)) && entry.isMandatory) { + return false; + } + } + return true; + } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_APPS) { + for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) { + if (packageName.equals(entry.packageName)) { + return false; + } + } + return true; + } + return false; + } + + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public int getToggleSensorPrivacyState(int toggleType, int sensor) { + if (DEBUG) { + Log.d(TAG, "callingUid=" + Binder.getCallingUid() + + " callingPid=" + Binder.getCallingPid() + + " getToggleSensorPrivacyState(" + + "toggleType=" + toggleType + + " sensor=" + sensor + + ")"); + } + enforceObserveSensorPrivacyPermission(); + + return mSensorPrivacyStateController.getState(toggleType, mCurrentUser, sensor) + .getState(); + } + private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source, int sensor, boolean enable) { if (DEBUG) { @@ -899,16 +1181,23 @@ public final class SensorPrivacyService extends SystemService { * Enforces the caller contains the necessary permission to change the state of sensor * privacy. */ + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) private void enforceManageSensorPrivacyPermission() { - enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY, - "Changing sensor privacy requires the following permission: " - + MANAGE_SENSOR_PRIVACY); + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { + return; + } + + String message = "Changing sensor privacy requires the following permission: " + + MANAGE_SENSOR_PRIVACY; + throw new SecurityException(message); } /** * Enforces the caller contains the necessary permission to observe changes to the sate of * sensor privacy. */ + @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) private void enforceObserveSensorPrivacyPermission() { String systemUIPackage = mContext.getString(R.string.config_systemUi); int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal @@ -917,15 +1206,13 @@ public final class SensorPrivacyService extends SystemService { // b/221782106, possible race condition with role grant might bootloop device. return; } - enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY, - "Observing sensor privacy changes requires the following permission: " - + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY); - } - - private void enforcePermission(String permission, String message) { - if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { return; } + + String message = "Observing sensor privacy changes requires the following permission: " + + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY; throw new SecurityException(message); } @@ -1293,11 +1580,13 @@ public final class SensorPrivacyService extends SystemService { } @Override + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { (new ShellCommand() { @Override + @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) public int onCommand(String cmd) { if (cmd == null) { return handleDefaultCommands(cmd); @@ -1327,6 +1616,45 @@ public final class SensorPrivacyService extends SystemService { setToggleSensorPrivacy(userId, SHELL, sensor, false); } break; + case "automotive_driver_assistance_apps" : { + if (Flags.cameraPrivacyAllowlist()) { + int sensor = sensorStrToId(getNextArgRequired()); + if ((!isAutomotive(mContext)) || (sensor != CAMERA)) { + pw.println("Command not valid for this sensor"); + return -1; + } + + setToggleSensorPrivacyState(userId, SHELL, sensor, + AUTOMOTIVE_DRIVER_ASSISTANCE_APPS); + } + } + break; + case "automotive_driver_assistance_helpful_apps" : { + if (Flags.cameraPrivacyAllowlist()) { + int sensor = sensorStrToId(getNextArgRequired()); + if ((!isAutomotive(mContext)) || (sensor != CAMERA)) { + pw.println("Command not valid for this sensor"); + return -1; + } + + setToggleSensorPrivacyState(userId, SHELL, sensor, + AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS); + } + } + break; + case "automotive_driver_assistance_required_apps" : { + if (Flags.cameraPrivacyAllowlist()) { + int sensor = sensorStrToId(getNextArgRequired()); + if ((!isAutomotive(mContext)) || (sensor != CAMERA)) { + pw.println("Command not valid for this sensor"); + return -1; + } + + setToggleSensorPrivacyState(userId, SHELL, sensor, + AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS); + } + } + break; default: return handleDefaultCommands(cmd); } @@ -1349,6 +1677,24 @@ public final class SensorPrivacyService extends SystemService { pw.println(" disable USER_ID SENSOR"); pw.println(" Disable privacy for a certain sensor."); pw.println(""); + if (Flags.cameraPrivacyAllowlist()) { + if (isAutomotive(mContext)) { + pw.println(" automotive_driver_assistance_apps USER_ID SENSOR"); + pw.println(" Disable privacy for automotive apps which help you" + + " drive and apps which are required by OEM"); + pw.println(""); + pw.println(" automotive_driver_assistance_helpful_apps " + + "USER_ID SENSOR"); + pw.println(" Disable privacy for automotive apps which " + + "help you drive."); + pw.println(""); + pw.println(" automotive_driver_assistance_required_apps " + + "USER_ID SENSOR"); + pw.println(" Disable privacy for automotive apps which are " + + "required by OEM."); + pw.println(""); + } + } } }).exec(this, in, out, err, args, callback, resultReceiver); } @@ -1457,6 +1803,38 @@ public final class SensorPrivacyService extends SystemService { mSensorPrivacyServiceImpl.showSensorStateChangedActivity(sensor, toggleType); } + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + public void handleSensorPrivacyChanged(int userId, int toggleType, int sensor, + int state) { + if (userId == mCurrentUser) { + mSensorPrivacyServiceImpl.setGlobalRestriction(sensor, + mSensorPrivacyServiceImpl.isCombinedToggleSensorPrivacyEnabled(sensor)); + } + + if (userId != mCurrentUser) { + return; + } + synchronized (mListenerLock) { + try { + final int count = mToggleSensorListeners.beginBroadcast(); + for (int i = 0; i < count; i++) { + ISensorPrivacyListener listener = mToggleSensorListeners.getBroadcastItem( + i); + try { + listener.onSensorPrivacyStateChanged(toggleType, sensor, state); + } catch (RemoteException e) { + Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", + e); + } + } + } finally { + mToggleSensorListeners.finishBroadcast(); + } + } + + mSensorPrivacyServiceImpl.showSensorStateChangedActivity(sensor, toggleType); + } + public void removeSuppressPackageReminderToken(Pair<Integer, UserHandle> key, IBinder token) { sendMessage(PooledLambda.obtainMessage( diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java index 96949589447c..14b13e0d3692 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java @@ -16,9 +16,11 @@ package com.android.server.sensorprivacy; +import android.annotation.FlaggedApi; import android.os.Handler; import com.android.internal.annotations.GuardedBy; +import com.android.internal.camera.flags.Flags; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.internal.util.function.pooled.PooledLambda; @@ -51,6 +53,14 @@ abstract class SensorPrivacyStateController { } } + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + void setState(int toggleType, int userId, int sensor, int state, Handler callbackHandler, + SetStateResultCallback callback) { + synchronized (mLock) { + setStateLocked(toggleType, userId, sensor, state, callbackHandler, callback); + } + } + void setSensorPrivacyListener(Handler handler, SensorPrivacyListener listener) { synchronized (mLock) { @@ -128,6 +138,11 @@ abstract class SensorPrivacyStateController { Handler callbackHandler, SetStateResultCallback callback); @GuardedBy("mLock") + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + abstract void setStateLocked(int toggleType, int userId, int sensor, int state, + Handler callbackHandler, SetStateResultCallback callback); + + @GuardedBy("mLock") abstract void setSensorPrivacyListenerLocked(Handler handler, SensorPrivacyListener listener); diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java index 3dcb4cf996c4..4b491ce30923 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java @@ -16,8 +16,12 @@ package com.android.server.sensorprivacy; +import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED; + +import android.annotation.FlaggedApi; import android.os.Handler; +import com.android.internal.camera.flags.Flags; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.internal.util.function.pooled.PooledLambda; @@ -85,6 +89,33 @@ class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController { sendSetStateCallback(callbackHandler, callback, false); } + @Override + @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST) + void setStateLocked(int toggleType, int userId, int sensor, int state, + Handler callbackHandler, SetStateResultCallback callback) { + // Changing the SensorState's mEnabled updates the timestamp of its last change. + // A nonexistent state -> unmuted should not set the timestamp. + SensorState lastState = mPersistedState.getState(toggleType, userId, sensor); + if (lastState == null) { + if (state == DISABLED) { + sendSetStateCallback(callbackHandler, callback, false); + return; + } else { + SensorState sensorState = new SensorState(state); + mPersistedState.setState(toggleType, userId, sensor, sensorState); + notifyStateChangeLocked(toggleType, userId, sensor, sensorState); + sendSetStateCallback(callbackHandler, callback, true); + return; + } + } + if (lastState.setState(state)) { + notifyStateChangeLocked(toggleType, userId, sensor, lastState); + sendSetStateCallback(callbackHandler, callback, true); + return; + } + sendSetStateCallback(callbackHandler, callback, false); + } + private void notifyStateChangeLocked(int toggleType, int userId, int sensor, SensorState sensorState) { if (mListenerHandler != null && mListener != null) { diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index d2f6701e313e..4af8c616b2bc 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -65,6 +65,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; import android.net.Uri; +import android.os.BadParcelableException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -630,16 +631,24 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements if (intent == null) { return null; } - Uri data = intent.getData(); - ClipData clip = intent.getClipData(); - if (data == null && clip == null) { - return null; - } + // Default userId for uris in the intent (if they don't specify it themselves) int contentUserHint = intent.getContentUserHint(); if (contentUserHint == UserHandle.USER_CURRENT) { contentUserHint = UserHandle.getUserId(callingUid); } + + if (android.security.Flags.contentUriPermissionApis()) { + enforceRequireContentUriPermissionFromCallerOnIntentExtraStream(intent, contentUserHint, + mode, callingUid, requireContentUriPermissionFromCaller); + } + + Uri data = intent.getData(); + ClipData clip = intent.getClipData(); + if (data == null && clip == null) { + return null; + } + int targetUid; if (needed != null) { targetUid = needed.targetUid; @@ -733,6 +742,37 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements } } + private void enforceRequireContentUriPermissionFromCallerOnIntentExtraStream(Intent intent, + int contentUserHint, int mode, int callingUid, + @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller) { + try { + final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class); + if (uri != null) { + final GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode); + enforceRequireContentUriPermissionFromCaller( + requireContentUriPermissionFromCaller, grantUri, callingUid); + } + } catch (BadParcelableException e) { + Slog.w(TAG, "Failed to unparcel an URI in EXTRA_STREAM, skipping" + + " requireContentUriPermissionFromCaller: " + e); + } + + try { + final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, + Uri.class); + if (uris != null) { + for (int i = uris.size() - 1; i >= 0; i--) { + final GrantUri grantUri = GrantUri.resolve(contentUserHint, uris.get(i), mode); + enforceRequireContentUriPermissionFromCaller( + requireContentUriPermissionFromCaller, grantUri, callingUid); + } + } + } catch (BadParcelableException e) { + Slog.w(TAG, "Failed to unparcel an ArrayList of URIs in EXTRA_STREAM, skipping" + + " requireContentUriPermissionFromCaller: " + e); + } + } + @GuardedBy("mLock") private void readGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()"); diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java index 4772bbfe97dd..2e1049b9ea32 100644 --- a/services/core/java/com/android/server/utils/EventLogger.java +++ b/services/core/java/com/android/server/utils/EventLogger.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; +import android.util.Slog; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -84,6 +85,17 @@ public class EventLogger { enqueue(event.printLog(logType, tag)); } + /** + * Add a string-based event to the system log, and print it to the log with a specific severity. + * @param msg the message to appear in the log + * @param logType the log severity (verbose/info/warning/error) + * @param tag the tag under which the log entry will appear + */ + public synchronized void enqueueAndSlog(String msg, @Event.LogType int logType, String tag) { + final Event event = new StringEvent(msg); + enqueue(event.printSlog(logType, tag)); + } + /** Dumps events into the given {@link DumpSink}. */ public synchronized void dump(DumpSink dumpSink) { dumpSink.sink(mTag, new ArrayList<>(mEvents)); @@ -138,7 +150,7 @@ public class EventLogger { /** * Causes the string message for the event to appear in the logcat. * Here is an example of how to create a new event (a StringEvent), adding it to the logger - * (an instance of AudioEventLogger) while also making it show in the logcat: + * (an instance of EventLogger) while also making it show in the logcat: * <pre> * myLogger.log( * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) ); @@ -167,9 +179,9 @@ public class EventLogger { /** * Same as {@link #printLog(String)} with a log type - * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV} - * @param tag - * @return + * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}, {@link #ALOGW} + * @param tag the tag the log entry will be printed under + * @return the event itself */ public Event printLog(@LogType int type, String tag) { switch (type) { @@ -191,6 +203,32 @@ public class EventLogger { } /** + * Causes the string message for the event to appear in the system log. + * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}, {@link #ALOGW} + * @param tag the tag the log entry will be printed under + * @return the event itself + * @see #printLog(int, String) + */ + public Event printSlog(@LogType int type, String tag) { + switch (type) { + case ALOGI: + Slog.i(tag, eventToString()); + break; + case ALOGE: + Slog.e(tag, eventToString()); + break; + case ALOGW: + Slog.w(tag, eventToString()); + break; + case ALOGV: + default: + Slog.v(tag, eventToString()); + break; + } + return this; + } + + /** * Convert event to String. * This method is only called when the logger history is about to the dumped, * so this method is where expensive String conversions should be made, not when the Event diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING index 92b327d9abff..c64941bebbf4 100644 --- a/services/core/java/com/android/server/vibrator/TEST_MAPPING +++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING @@ -5,6 +5,9 @@ }, { "path": "cts/tests/vibrator" + }, + { + "path": "frameworks/hardware/interfaces/vibrator/aidl" } ] } diff --git a/services/core/java/com/android/server/wm/ActivityCallerState.java b/services/core/java/com/android/server/wm/ActivityCallerState.java index 4416605d9f04..e7972904adaa 100644 --- a/services/core/java/com/android/server/wm/ActivityCallerState.java +++ b/services/core/java/com/android/server/wm/ActivityCallerState.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; + import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -51,6 +53,9 @@ final class ActivityCallerState { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityCallerState" : TAG_ATM; // XML tags for CallerInfo + private static final String ATTR_CALLER_UID = "caller_uid"; + private static final String ATTR_CALLER_PACKAGE = "caller_package"; + private static final String ATTR_CALLER_IS_SHARE_ENABLED = "caller_is_share_enabled"; private static final String TAG_READABLE_CONTENT_URI = "readable_content_uri"; private static final String TAG_WRITABLE_CONTENT_URI = "writable_content_uri"; private static final String TAG_INACCESSIBLE_CONTENT_URI = "inaccessible_content_uri"; @@ -71,12 +76,33 @@ final class ActivityCallerState { return mCallerTokenInfoMap.getOrDefault(callerToken, null); } + boolean hasCaller(IBinder callerToken) { + return getCallerInfoOrNull(callerToken) != null; + } + + int getUid(IBinder callerToken) { + CallerInfo callerInfo = getCallerInfoOrNull(callerToken); + return callerInfo != null ? callerInfo.mUid : INVALID_UID; + } + + String getPackage(IBinder callerToken) { + CallerInfo callerInfo = getCallerInfoOrNull(callerToken); + return callerInfo != null ? callerInfo.mPackageName : null; + } + + boolean isShareIdentityEnabled(IBinder callerToken) { + CallerInfo callerInfo = getCallerInfoOrNull(callerToken); + return callerInfo != null ? callerInfo.mIsShareIdentityEnabled : false; + } + void add(IBinder callerToken, CallerInfo callerInfo) { mCallerTokenInfoMap.put(callerToken, callerInfo); } - void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) { - final CallerInfo callerInfo = new CallerInfo(); + void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid, + String callerPackageName, boolean isCallerShareIdentityEnabled) { + final CallerInfo callerInfo = new CallerInfo(callerUid, callerPackageName, + isCallerShareIdentityEnabled); mCallerTokenInfoMap.put(callerToken, callerInfo); final ArraySet<Uri> contentUris = getContentUrisFromIntent(intent); @@ -180,12 +206,26 @@ final class ActivityCallerState { } public static final class CallerInfo { + final int mUid; + final String mPackageName; + final boolean mIsShareIdentityEnabled; final ArraySet<GrantUri> mReadableContentUris = new ArraySet<>(); final ArraySet<GrantUri> mWritableContentUris = new ArraySet<>(); final ArraySet<GrantUri> mInaccessibleContentUris = new ArraySet<>(); + CallerInfo(int uid, String packageName, boolean isShareIdentityEnabled) { + mUid = uid; + mPackageName = packageName; + mIsShareIdentityEnabled = isShareIdentityEnabled; + } + public void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException { + out.attributeInt(null, ATTR_CALLER_UID, mUid); + if (mPackageName != null) { + out.attribute(null, ATTR_CALLER_PACKAGE, mPackageName); + } + out.attributeBoolean(null, ATTR_CALLER_IS_SHARE_ENABLED, mIsShareIdentityEnabled); for (int i = mReadableContentUris.size() - 1; i >= 0; i--) { saveGrantUriToXml(out, mReadableContentUris.valueAt(i), TAG_READABLE_CONTENT_URI); } @@ -202,7 +242,12 @@ final class ActivityCallerState { public static CallerInfo restoreFromXml(TypedXmlPullParser in) throws IOException, XmlPullParserException { - CallerInfo callerInfo = new CallerInfo(); + int uid = in.getAttributeInt(null, ATTR_CALLER_UID, 0); + String packageName = in.getAttributeValue(null, ATTR_CALLER_PACKAGE); + boolean isShareIdentityEnabled = in.getAttributeBoolean(null, + ATTR_CALLER_IS_SHARE_ENABLED, false); + + CallerInfo callerInfo = new CallerInfo(uid, packageName, isShareIdentityEnabled); final int outerDepth = in.getDepth(); int event; while (((event = in.next()) != END_DOCUMENT) diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index efaa467e43e4..ed5df5fab017 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -695,30 +695,57 @@ class ActivityClientController extends IActivityClientController.Stub { @Override public int getLaunchedFromUid(IBinder token) { + return getUid(token, /* callerToken */ null, /* isActivityCallerCall */ false); + } + + @Override + public String getLaunchedFromPackage(IBinder token) { + return getPackage(token, /* callerToken */ null, /* isActivityCallerCall */ false); + } + + @Override + public int getActivityCallerUid(IBinder activityToken, IBinder callerToken) { + return getUid(activityToken, callerToken, /* isActivityCallerCall */ true); + } + + @Override + public String getActivityCallerPackage(IBinder activityToken, IBinder callerToken) { + return getPackage(activityToken, callerToken, /* isActivityCallerCall */ true); + } + + private int getUid(IBinder activityToken, IBinder callerToken, boolean isActivityCallerCall) { final int uid = Binder.getCallingUid(); final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid); synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) { - return r.launchedFromUid; + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r, callerToken, + isActivityCallerCall)) && isValidCaller(r, callerToken, isActivityCallerCall)) { + return isActivityCallerCall ? r.getCallerUid(callerToken) : r.launchedFromUid; } } return INVALID_UID; } - @Override - public String getLaunchedFromPackage(IBinder token) { + private String getPackage(IBinder activityToken, IBinder callerToken, + boolean isActivityCallerCall) { final int uid = Binder.getCallingUid(); final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid); synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) { - return r.launchedFromPackage; + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r, callerToken, + isActivityCallerCall)) && isValidCaller(r, callerToken, isActivityCallerCall)) { + return isActivityCallerCall + ? r.getCallerPackage(callerToken) : r.launchedFromPackage; } } return null; } + private boolean isValidCaller(ActivityRecord r, IBinder callerToken, + boolean isActivityCallerCall) { + return isActivityCallerCall ? r.hasCaller(callerToken) : callerToken == null; + } + /** * @param uri This uri must NOT contain an embedded userId. * @param userId The userId in which the uri is to be resolved. @@ -768,9 +795,13 @@ class ActivityClientController extends IActivityClientController.Stub { * verifying whether the provided {@code ActivityRecord r} has opted in to sharing its * identity or if the uid of the activity matches that of the launching app. */ - private static boolean canGetLaunchedFromLocked(int uid, ActivityRecord r) { + private static boolean canGetLaunchedFromLocked(int uid, ActivityRecord r, + IBinder callerToken, boolean isActivityCallerCall) { if (CompatChanges.isChangeEnabled(ACCESS_SHARED_IDENTITY, uid)) { - return r.mShareIdentity || r.launchedFromUid == uid; + boolean isShareIdentityEnabled = isActivityCallerCall + ? r.isCallerShareIdentityEnabled(callerToken) : r.mShareIdentity; + int callerUid = isActivityCallerCall ? r.getCallerUid(callerToken) : r.launchedFromUid; + return isShareIdentityEnabled || callerUid == uid; } return false; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 09c329be7d09..c36df8da77ae 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2024,12 +2024,31 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + boolean hasCaller(IBinder callerToken) { + return mCallerState.hasCaller(callerToken); + } + + int getCallerUid(IBinder callerToken) { + return mCallerState.getUid(callerToken); + } + + String getCallerPackage(IBinder callerToken) { + return mCallerState.getPackage(callerToken); + } + + boolean isCallerShareIdentityEnabled(IBinder callerToken) { + return mCallerState.isShareIdentityEnabled(callerToken); + } + void computeInitialCallerInfo() { - computeCallerInfo(initialCallerInfoAccessToken, intent, launchedFromUid); + computeCallerInfo(initialCallerInfoAccessToken, intent, launchedFromUid, + launchedFromPackage, mShareIdentity); } - void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) { - mCallerState.computeCallerInfo(callerToken, intent, callerUid); + void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid, + String callerPackageName, boolean isCallerShareIdentityEnabled) { + mCallerState.computeCallerInfo(callerToken, intent, callerUid, callerPackageName, + isCallerShareIdentityEnabled); } boolean checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags) { @@ -3526,6 +3545,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants, resultTo.getUriPermissionsLocked()); } + IBinder callerToken = new Binder(); + if (android.security.Flags.contentUriPermissionApis()) { + try { + resultTo.computeCallerInfo(callerToken, intent, this.getUid(), + mAtmService.getPackageManager().getNameForUid(this.getUid()), + /* isShareIdentityEnabled */ false); + // Result callers cannot share their identity via + // {@link ActivityOptions#setShareIdentityEnabled(boolean)} since + // {@link android.app.Activity#setResult} doesn't have a + // {@link android.os.Bundle}. + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } if (mForceSendResultForMediaProjection || resultTo.isState(RESUMED)) { // Sending the result to the resultTo activity asynchronously to prevent the // resultTo activity getting results before this Activity paused. @@ -3533,12 +3566,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.mH.post(() -> { synchronized (mAtmService.mGlobalLock) { resultToActivity.sendResult(this.getUid(), resultWho, requestCode, - resultCode, resultData, resultGrants, + resultCode, resultData, callerToken, resultGrants, mForceSendResultForMediaProjection); } }); } else { - resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData); + resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData, + callerToken); } resultTo = null; } else if (DEBUG_RESULTS) { @@ -4822,9 +4856,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void addResultLocked(ActivityRecord from, String resultWho, int requestCode, int resultCode, - Intent resultData) { + Intent resultData, IBinder callerToken) { ActivityResult r = new ActivityResult(from, resultWho, - requestCode, resultCode, resultData); + requestCode, resultCode, resultData, callerToken); if (results == null) { results = new ArrayList<ResultInfo>(); } @@ -4850,13 +4884,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void sendResult(int callingUid, String resultWho, int requestCode, int resultCode, - Intent data, NeededUriGrants dataGrants) { - sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants, + Intent data, IBinder callerToken, NeededUriGrants dataGrants) { + sendResult(callingUid, resultWho, requestCode, resultCode, data, callerToken, dataGrants, false /* forceSendForMediaProjection */); } void sendResult(int callingUid, String resultWho, int requestCode, int resultCode, - Intent data, NeededUriGrants dataGrants, boolean forceSendForMediaProjection) { + Intent data, IBinder callerToken, NeededUriGrants dataGrants, + boolean forceSendForMediaProjection) { + if (android.security.Flags.contentUriPermissionApis() + && !mCallerState.hasCaller(callerToken)) { + try { + computeCallerInfo(callerToken, data, callingUid, + mAtmService.getPackageManager().getNameForUid(callingUid), + false /* isShareIdentityEnabled */); + // Result callers cannot share their identity via + // {@link ActivityOptions#setShareIdentityEnabled(boolean)} since + // {@link android.app.Activity#setResult} doesn't have a {@link android.os.Bundle}. + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } if (callingUid > 0) { mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants, getUriPermissionsLocked()); @@ -4872,7 +4920,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isState(RESUMED) && attachedToProcess()) { try { final ArrayList<ResultInfo> list = new ArrayList<>(); - list.add(new ResultInfo(resultWho, requestCode, resultCode, data)); + list.add(new ResultInfo(resultWho, requestCode, resultCode, data, callerToken)); mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), ActivityResultItem.obtain(token, list)); return; @@ -4886,7 +4934,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A STOPPING, STOPPED)) { // Build result to be returned immediately. final ActivityResultItem activityResultItem = ActivityResultItem.obtain( - token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data))); + token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data, + callerToken))); // When the activity result is delivered, the activity will transition to RESUMED. // Since the activity is only resumed so the result can be immediately delivered, // return it to its original lifecycle state. @@ -4910,7 +4959,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - addResultLocked(null /* from */, resultWho, requestCode, resultCode, data); + addResultLocked(null /* from */, resultWho, requestCode, resultCode, data, callerToken); } /** @@ -4960,11 +5009,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * method will be called at the proper time. */ final void deliverNewIntentLocked(int callingUid, Intent intent, NeededUriGrants intentGrants, - String referrer) { + String referrer, boolean isShareIdentityEnabled) { + IBinder callerToken = new Binder(); + if (android.security.Flags.contentUriPermissionApis()) { + computeCallerInfo(callerToken, intent, callingUid, referrer, isShareIdentityEnabled); + } // The activity now gets access to the data associated with this Intent. mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants, getUriPermissionsLocked()); - final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer)); + final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer), + callerToken); boolean unsent = true; final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping(); diff --git a/services/core/java/com/android/server/wm/ActivityResult.java b/services/core/java/com/android/server/wm/ActivityResult.java index f2510de6dfb2..c5c51b50961c 100644 --- a/services/core/java/com/android/server/wm/ActivityResult.java +++ b/services/core/java/com/android/server/wm/ActivityResult.java @@ -18,16 +18,17 @@ package com.android.server.wm; import android.app.ResultInfo; import android.content.Intent; +import android.os.IBinder; /** * Pending result information to send back to an activity. */ final class ActivityResult extends ResultInfo { final ActivityRecord mFrom; - + public ActivityResult(ActivityRecord from, String resultWho, - int requestCode, int resultCode, Intent data) { - super(resultWho, requestCode, resultCode, data); + int requestCode, int resultCode, Intent data, IBinder callerToken) { + super(resultWho, requestCode, resultCode, data, callerToken); mFrom = from; } } diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java index db27f607c867..01d077a5bc55 100644 --- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java +++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java @@ -22,12 +22,10 @@ import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS; import android.annotation.NonNull; import android.app.compat.CompatChanges; -import android.content.pm.PackageManager; import android.provider.DeviceConfig; import com.android.internal.annotations.GuardedBy; -import java.util.HashSet; import java.util.concurrent.Executor; /** @@ -50,74 +48,49 @@ class ActivitySecurityModelFeatureFlags { private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX + "asm_restrictions_enabled"; private static final String KEY_ASM_TOASTS_ENABLED = KEY_ASM_PREFIX + "asm_toasts_enabled"; - private static final String KEY_ASM_EXEMPTED_PACKAGES = KEY_ASM_PREFIX - + "asm_exempted_packages"; + private static final int VALUE_DISABLE = 0; private static final int VALUE_ENABLE_FOR_V = 1; private static final int VALUE_ENABLE_FOR_ALL = 2; private static final int DEFAULT_VALUE = VALUE_DISABLE; - private static final String DEFAULT_EXCEPTION_LIST = ""; private static int sAsmToastsEnabled; private static int sAsmRestrictionsEnabled; - private static final HashSet<String> sExcludedPackageNames = new HashSet<>(); - private static PackageManager sPm; @GuardedBy("ActivityTaskManagerService.mGlobalLock") - static void initialize(@NonNull Executor executor, @NonNull PackageManager pm) { + static void initialize(@NonNull Executor executor) { updateFromDeviceConfig(); DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor, properties -> updateFromDeviceConfig()); - sPm = pm; } @GuardedBy("ActivityTaskManagerService.mGlobalLock") static boolean shouldShowToast(int uid) { - return flagEnabledForUid(sAsmToastsEnabled, uid); + return sAsmToastsEnabled == VALUE_ENABLE_FOR_ALL + || (sAsmToastsEnabled == VALUE_ENABLE_FOR_V + && CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid)); } @GuardedBy("ActivityTaskManagerService.mGlobalLock") static boolean shouldRestrictActivitySwitch(int uid) { - return flagEnabledForUid(sAsmRestrictionsEnabled, uid); - } - - private static boolean flagEnabledForUid(int flag, int uid) { - boolean flagEnabled = flag == VALUE_ENABLE_FOR_ALL - || (flag == VALUE_ENABLE_FOR_V - && CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid)); - - if (flagEnabled) { - String[] packageNames = sPm.getPackagesForUid(uid); - if (packageNames == null) { - return true; - } - for (int i = 0; i < packageNames.length; i++) { - if (sExcludedPackageNames.contains(packageNames[i])) { - return false; - } - } - return true; + if (android.security.Flags.asmRestrictionsEnabled()) { + return CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid) + || asmRestrictionsEnabledForAll(); } return false; } + @GuardedBy("ActivityTaskManagerService.mGlobalLock") + static boolean asmRestrictionsEnabledForAll() { + return sAsmRestrictionsEnabled == VALUE_ENABLE_FOR_ALL; + } + private static void updateFromDeviceConfig() { sAsmToastsEnabled = DeviceConfig.getInt(NAMESPACE, KEY_ASM_TOASTS_ENABLED, DEFAULT_VALUE); sAsmRestrictionsEnabled = DeviceConfig.getInt(NAMESPACE, KEY_ASM_RESTRICTIONS_ENABLED, DEFAULT_VALUE); - - String rawExceptionList = DeviceConfig.getString(NAMESPACE, - KEY_ASM_EXEMPTED_PACKAGES, DEFAULT_EXCEPTION_LIST); - sExcludedPackageNames.clear(); - String[] packages = rawExceptionList.split(","); - for (String packageName : packages) { - String packageNameTrimmed = packageName.trim(); - if (!packageNameTrimmed.isEmpty()) { - sExcludedPackageNames.add(packageNameTrimmed); - } - } } } diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java index a54dd826b5bb..3609837f417b 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java @@ -23,18 +23,20 @@ import android.window.TaskSnapshot; */ class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> { - ActivitySnapshotCache(WindowManagerService service) { - super(service, "Activity"); + ActivitySnapshotCache() { + super("Activity"); } @Override void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) { final int hasCode = System.identityHashCode(ar); - final CacheEntry entry = mRunningCache.get(hasCode); - if (entry != null) { - mAppIdMap.remove(entry.topApp); + synchronized (mLock) { + final CacheEntry entry = mRunningCache.get(hasCode); + if (entry != null) { + mAppIdMap.remove(entry.topApp); + } + mAppIdMap.put(ar, hasCode); + mRunningCache.put(hasCode, new CacheEntry(snapshot, ar)); } - mAppIdMap.put(ar, hasCode); - mRunningCache.put(hasCode, new CacheEntry(snapshot, ar)); } } diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 1f013b913283..f83003d4e278 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -102,7 +102,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord Environment::getDataSystemCeDirectory); mPersister = new TaskSnapshotPersister(persistQueue, mPersistInfoProvider); mSnapshotLoader = new AppSnapshotLoader(mPersistInfoProvider); - initialize(new ActivitySnapshotCache(service)); + initialize(new ActivitySnapshotCache()); final boolean snapshotEnabled = !service.mContext diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 07afa5fc21be..cfd049508e65 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1103,7 +1103,7 @@ class ActivityStarter { if (err != START_SUCCESS) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); } SafeActivityOptions.abort(options); return err; @@ -1128,7 +1128,8 @@ class ActivityStarter { .filterAppAccess(targetPackageName, callingUid, userId)) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, - RESULT_CANCELED, null /* data */, null /* dataGrants */); + RESULT_CANCELED, null /* data */, null /* callerToken */, + null /* dataGrants */); } SafeActivityOptions.abort(options); return ActivityManager.START_CLASS_NOT_FOUND; @@ -1216,7 +1217,7 @@ class ActivityStarter { if (abort) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); } // We pretend to the caller that it was really started, but they will just get a // cancel result. @@ -1380,7 +1381,7 @@ class ActivityStarter { int requestCode = r.requestCode; if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); } // We pretend to the caller that it was really started to make it backward compatible, but // they will just get a cancel result. @@ -1727,7 +1728,7 @@ class ActivityStarter { if (startResult != START_SUCCESS) { if (r.resultTo != null) { r.resultTo.sendResult(INVALID_UID, r.resultWho, r.requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); } return startResult; } @@ -2226,7 +2227,7 @@ class ActivityStarter { if (mStartActivity.resultTo != null) { mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); mStartActivity.resultTo = null; } @@ -2607,7 +2608,7 @@ class ActivityStarter { Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); + null /* data */, null /* callerToken */, null /* dataGrants */); mStartActivity.resultTo = null; } } @@ -2909,7 +2910,7 @@ class ActivityStarter { activity.logStartActivity(EventLogTags.WM_NEW_INTENT, activity.getTask()); activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, intentGrants, - mStartActivity.launchedFromPackage); + mStartActivity.launchedFromPackage, mStartActivity.mShareIdentity); mIntentDelivered = true; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0e6c06dad486..52fdfda7acfe 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -881,7 +881,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTaskSupervisor.onSystemReady(); mActivityClientController.onSystemReady(); // TODO(b/258792202) Cleanup once ASM is ready to launch - ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm); + ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor()); mGrammaticalManagerInternal = LocalServices.getService( GrammaticalInflectionManagerInternal.class); } @@ -6334,7 +6334,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final NeededUriGrants dataGrants = collectGrants(data, r); synchronized (mGlobalLock) { - r.sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants); + r.sendResult(callingUid, resultWho, requestCode, resultCode, data, new Binder(), + dataGrants); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 49df39664b1c..09f5eda5b571 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1118,7 +1118,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, - Activity.RESULT_CANCELED, null /* data */, null /* dataGrants */); + Activity.RESULT_CANCELED, null /* data */, null /* callerToken */, + null /* dataGrants */); } final String msg; if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) { diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index b65b2b2ae959..3bc531950128 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -56,6 +56,7 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Process; import android.os.SystemClock; @@ -529,6 +530,9 @@ public class BackgroundActivityStartController { static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked"); static final BalVerdict ALLOW_BY_DEFAULT = new BalVerdict(BAL_ALLOW_DEFAULT, false, "Default"); + // Careful using this - it will bypass all ASM checks. + static final BalVerdict ALLOW_PRIVILEGED = + new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, false, "PRIVILEGED"); private final @BalCode int mCode; private final boolean mBackground; private final String mMessage; @@ -913,8 +917,10 @@ public class BackgroundActivityStartController { // Normal apps with visible app window will be allowed to start activity if app switching // is allowed, or apps like live wallpaper with non app visible window will be allowed. + // The home app can start apps even if app switches are usually disallowed. final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW - || state.mAppSwitchState == APP_SWITCH_FG_ONLY; + || state.mAppSwitchState == APP_SWITCH_FG_ONLY + || isHomeApp(state.mRealCallingUid, state.mRealCallingPackage); if (balImproveRealCallerVisibilityCheck()) { if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) { return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, @@ -1232,7 +1238,8 @@ public class BackgroundActivityStartController { boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags .shouldRestrictActivitySwitch(callingUid); int[] finishCount = new int[0]; - if (shouldBlockActivityStart) { + if (shouldBlockActivityStart + && blockCrossUidActivitySwitchFromBelowForActivity(targetTaskTop)) { ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched); if (activity == null) { // mStartActivity is not in task, so clear everything @@ -1317,7 +1324,7 @@ public class BackgroundActivityStartController { boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags .shouldRestrictActivitySwitch(callingUid) - && bas.mBlockActivityStartIfFlagEnabled; + && bas.mBlockActivityStartIfFlagEnabled; PackageManager pm = mService.mContext.getPackageManager(); String callingPackage = pm.getNameForUid(callingUid); @@ -1371,19 +1378,19 @@ public class BackgroundActivityStartController { int uid, @Nullable ActivityRecord sourceRecord) { // If the source is visible, consider it 'top'. if (sourceRecord != null && sourceRecord.isVisibleRequested()) { - return new BlockActivityStart(false, false); + return BlockActivityStart.ACTIVITY_START_ALLOWED; } // Always allow actual top activity to clear task ActivityRecord topActivity = task.getTopMostActivity(); if (topActivity != null && topActivity.isUid(uid)) { - return new BlockActivityStart(false, false); + return BlockActivityStart.ACTIVITY_START_ALLOWED; } // If UID is visible in target task, allow launch if (task.forAllActivities((Predicate<ActivityRecord>) ar -> ar.isUid(uid) && ar.isVisibleRequested())) { - return new BlockActivityStart(false, false); + return BlockActivityStart.ACTIVITY_START_ALLOWED; } // Consider the source activity, whether or not it is finishing. Do not consider any other @@ -1450,12 +1457,11 @@ public class BackgroundActivityStartController { private BlockActivityStart blockCrossUidActivitySwitchFromBelow(ActivityRecord ar, int sourceUid) { if (ar.isUid(sourceUid)) { - return new BlockActivityStart(false, false); + return BlockActivityStart.ACTIVITY_START_ALLOWED; } - // If mAllowCrossUidActivitySwitchFromBelow is set, honor it. - if (ar.mAllowCrossUidActivitySwitchFromBelow) { - return new BlockActivityStart(false, false); + if (!blockCrossUidActivitySwitchFromBelowForActivity(ar)) { + return BlockActivityStart.ACTIVITY_START_ALLOWED; } // At this point, we would block if the feature is launched and both apps were V+ @@ -1466,8 +1472,11 @@ public class BackgroundActivityStartController { ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(ar.getUid()) && ActivitySecurityModelFeatureFlags .shouldRestrictActivitySwitch(sourceUid); - return new BackgroundActivityStartController - .BlockActivityStart(restrictActivitySwitch, true); + if (restrictActivitySwitch) { + return BlockActivityStart.BLOCK; + } else { + return BlockActivityStart.LOG_ONLY; + } } /** @@ -1673,14 +1682,52 @@ public class BackgroundActivityStartController { } } - static class BlockActivityStart { + /** + * Activity level allowCrossUidActivitySwitchFromBelow defaults to false. + * Package level defaults to true. + * We block the launch if dev has explicitly set package level to false, and activity level has + * not opted out + */ + private boolean blockCrossUidActivitySwitchFromBelowForActivity(@NonNull ActivityRecord ar) { + // We don't need to check package level if activity has opted out. + if (ar.mAllowCrossUidActivitySwitchFromBelow) { + return false; + } + + if (ActivitySecurityModelFeatureFlags.asmRestrictionsEnabledForAll()) { + return true; + } + + String packageName = ar.packageName; + if (packageName == null) { + return false; + } + + PackageManager pm = mService.mContext.getPackageManager(); + ApplicationInfo applicationInfo; + + try { + applicationInfo = pm.getApplicationInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf(TAG, "Package name: " + packageName + " not found."); + return false; + } + + return !applicationInfo.allowCrossUidActivitySwitchFromBelow; + } + + private static class BlockActivityStart { + private static final BlockActivityStart ACTIVITY_START_ALLOWED = + new BlockActivityStart(false, false); + private static final BlockActivityStart LOG_ONLY = new BlockActivityStart(false, true); + private static final BlockActivityStart BLOCK = new BlockActivityStart(true, true); // We should block if feature flag is enabled private final boolean mBlockActivityStartIfFlagEnabled; // Used for logging/toasts. Would we block if target sdk was V and feature was // enabled? private final boolean mWouldBlockActivityStartIgnoringFlag; - BlockActivityStart(boolean shouldBlockActivityStart, + private BlockActivityStart(boolean shouldBlockActivityStart, boolean wouldBlockActivityStartIgnoringFlags) { this.mBlockActivityStartIfFlagEnabled = shouldBlockActivityStart; this.mWouldBlockActivityStartIgnoringFlag = wouldBlockActivityStartIgnoringFlags; diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index e6ef90bd33d2..68bff439b712 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -17,6 +17,7 @@ package com.android.server.wm; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -34,6 +35,9 @@ import android.view.InputApplicationHandle; import android.view.InputChannel; import android.window.InputTransferToken; +import com.android.internal.protolog.common.ProtoLog; +import com.android.server.input.InputManagerService; + /** * Keeps track of embedded windows. * @@ -52,9 +56,13 @@ class EmbeddedWindowController { private final Object mGlobalLock; private final ActivityTaskManagerService mAtmService; - EmbeddedWindowController(ActivityTaskManagerService atmService) { + private final InputManagerService mInputManagerService; + + EmbeddedWindowController(ActivityTaskManagerService atmService, + InputManagerService inputManagerService) { mAtmService = atmService; mGlobalLock = atmService.getGlobalLock(); + mInputManagerService = inputManagerService; } /** @@ -135,6 +143,60 @@ class EmbeddedWindowController { return mWindowsByWindowToken.get(windowToken); } + private boolean isValidTouchGestureParams(WindowState hostWindowState, + EmbeddedWindow embeddedWindow) { + if (embeddedWindow == null) { + ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS, + "Attempt to transfer touch gesture with non-existent embedded window"); + return false; + } + final WindowState wsAssociatedWithEmbedded = embeddedWindow.getWindowState(); + if (wsAssociatedWithEmbedded == null) { + ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS, + "Attempt to transfer touch gesture using embedded window with no associated " + + "host"); + return false; + } + if (wsAssociatedWithEmbedded.mClient.asBinder() != hostWindowState.mClient.asBinder()) { + ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS, + "Attempt to transfer touch gesture with host window not associated with " + + "embedded window"); + return false; + } + + if (embeddedWindow.getInputChannelToken() == null) { + ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS, + "Attempt to transfer touch gesture using embedded window that has no input " + + "channel"); + return false; + } + if (hostWindowState.mInputChannelToken == null) { + ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS, + "Attempt to transfer touch gesture using a host window with no input channel"); + return false; + } + return true; + } + + boolean transferToHost(InputTransferToken embeddedWindowToken, + WindowState transferToHostWindowState) { + EmbeddedWindow ew = getByInputTransferToken(embeddedWindowToken); + if (!isValidTouchGestureParams(transferToHostWindowState, ew)) { + return false; + } + return mInputManagerService.transferTouchFocus(ew.getInputChannelToken(), + transferToHostWindowState.mInputChannelToken); + } + + boolean transferToEmbedded(WindowState hostWindowState, InputTransferToken transferToToken) { + final EmbeddedWindowController.EmbeddedWindow ew = getByInputTransferToken(transferToToken); + if (!isValidTouchGestureParams(hostWindowState, ew)) { + return false; + } + return mInputManagerService.transferTouchFocus(hostWindowState.mInputChannelToken, + ew.getInputChannelToken()); + } + static class EmbeddedWindow implements InputTarget { final IBinder mClient; @Nullable final WindowState mHostWindowState; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 609ad1e76370..bf45804192a9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -612,10 +612,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void refreshSecureSurfaceState() { - forAllWindows((w) -> { - if (w.mHasSurface) { - w.setSecureLocked(w.isSecureLocked()); - } + forAllWindows(w -> { + w.setSecureLocked(w.isSecureLocked()); }, true /* traverseTopToBottom */); } diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java index a7d6903bbe30..5fe48d12d498 100644 --- a/services/core/java/com/android/server/wm/SensitiveContentPackages.java +++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java @@ -56,6 +56,17 @@ public class SensitiveContentPackages { } /** + * Clears apps added to collection of apps in which screen capture should be disabled. + * + * @param packageInfos set of {@link PackageInfo} whose windows should be unblocked + * from capture. + * @return {@code true} if packages set is modified, {@code false} otherwise. + */ + public boolean removeBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos) { + return mProtectedPackages.removeAll(packageInfos); + } + + /** * Clears the set of package/uid pairs that should be blocked from screen capture * * @return {@code true} if packages set is modified, {@code false} otherwise. diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 3c8c55e278e3..975208fb4b4c 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -970,40 +970,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) { - if (embeddedWindow == null) { - return false; - } - - final long identity = Binder.clearCallingIdentity(); - boolean didTransfer = false; - try { - didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow); - } finally { - Binder.restoreCallingIdentity(identity); - } - return didTransfer; - } - - @Override - public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, - InputTransferToken inputTransferToken) { - if (hostWindow == null) { - return false; - } - - final long identity = Binder.clearCallingIdentity(); - boolean didTransfer; - try { - didTransfer = mService.transferHostTouchGestureToEmbedded(this, hostWindow, - inputTransferToken); - } finally { - Binder.restoreCallingIdentity(identity); - } - return didTransfer; - } - - @Override public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java index 401b2604c28f..86804360f6f4 100644 --- a/services/core/java/com/android/server/wm/SnapshotCache.java +++ b/services/core/java/com/android/server/wm/SnapshotCache.java @@ -19,6 +19,8 @@ import android.annotation.Nullable; import android.util.ArrayMap; import android.window.TaskSnapshot; +import com.android.internal.annotations.GuardedBy; + import java.io.PrintWriter; /** @@ -26,25 +28,31 @@ import java.io.PrintWriter; * @param <TYPE> The basic type, either Task or ActivityRecord */ abstract class SnapshotCache<TYPE extends WindowContainer> { - protected final WindowManagerService mService; + protected final Object mLock = new Object(); + protected final String mName; + + @GuardedBy("mLock") protected final ArrayMap<ActivityRecord, Integer> mAppIdMap = new ArrayMap<>(); + + @GuardedBy("mLock") protected final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>(); - SnapshotCache(WindowManagerService service, String name) { - mService = service; + SnapshotCache(String name) { mName = name; } abstract void putSnapshot(TYPE window, TaskSnapshot snapshot); void clearRunningCache() { - mRunningCache.clear(); + synchronized (mLock) { + mRunningCache.clear(); + } } @Nullable final TaskSnapshot getSnapshot(Integer id) { - synchronized (mService.mGlobalLock) { + synchronized (mLock) { // Try the running cache. final CacheEntry entry = mRunningCache.get(id); if (entry != null) { @@ -56,17 +64,21 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { /** Called when an app token has been removed. */ void onAppRemoved(ActivityRecord activity) { - final Integer id = mAppIdMap.get(activity); - if (id != null) { - removeRunningEntry(id); + synchronized (mLock) { + final Integer id = mAppIdMap.get(activity); + if (id != null) { + removeRunningEntry(id); + } } } /** Called when an app window token's process died. */ void onAppDied(ActivityRecord activity) { - final Integer id = mAppIdMap.get(activity); - if (id != null) { - removeRunningEntry(id); + synchronized (mLock) { + final Integer id = mAppIdMap.get(activity); + if (id != null) { + removeRunningEntry(id); + } } } @@ -75,10 +87,12 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { } void removeRunningEntry(Integer id) { - final CacheEntry entry = mRunningCache.get(id); - if (entry != null) { - mAppIdMap.remove(entry.topApp); - mRunningCache.remove(id); + synchronized (mLock) { + final CacheEntry entry = mRunningCache.get(id); + if (entry != null) { + mAppIdMap.remove(entry.topApp); + mRunningCache.remove(id); + } } } @@ -86,11 +100,14 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { final String doublePrefix = prefix + " "; final String triplePrefix = doublePrefix + " "; pw.println(prefix + "SnapshotCache " + mName); - for (int i = mRunningCache.size() - 1; i >= 0; i--) { - final CacheEntry entry = mRunningCache.valueAt(i); - pw.println(doublePrefix + "Entry token=" + mRunningCache.keyAt(i)); - pw.println(triplePrefix + "topApp=" + entry.topApp); - pw.println(triplePrefix + "snapshot=" + entry.snapshot); + + synchronized (mLock) { + for (int i = mRunningCache.size() - 1; i >= 0; i--) { + final CacheEntry entry = mRunningCache.valueAt(i); + pw.println(doublePrefix + "Entry token=" + mRunningCache.keyAt(i)); + pw.println(triplePrefix + "topApp=" + entry.topApp); + pw.println(triplePrefix + "snapshot=" + entry.snapshot); + } } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java index 33486ccb995f..b69ac1bb2795 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java @@ -28,19 +28,21 @@ class TaskSnapshotCache extends SnapshotCache<Task> { private final AppSnapshotLoader mLoader; - TaskSnapshotCache(WindowManagerService service, AppSnapshotLoader loader) { - super(service, "Task"); + TaskSnapshotCache(AppSnapshotLoader loader) { + super("Task"); mLoader = loader; } void putSnapshot(Task task, TaskSnapshot snapshot) { - final CacheEntry entry = mRunningCache.get(task.mTaskId); - if (entry != null) { - mAppIdMap.remove(entry.topApp); + synchronized (mLock) { + final CacheEntry entry = mRunningCache.get(task.mTaskId); + if (entry != null) { + mAppIdMap.remove(entry.topApp); + } + final ActivityRecord top = task.getTopMostActivity(); + mAppIdMap.put(top, task.mTaskId); + mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top)); } - final ActivityRecord top = task.getTopMostActivity(); - mAppIdMap.put(top, task.mTaskId); - mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top)); } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index d8e18e47fa89..8b622d28b651 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -68,7 +68,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot Environment::getDataSystemCeDirectory); mPersister = new TaskSnapshotPersister(persistQueue, mPersistInfoProvider); - initialize(new TaskSnapshotCache(service, new AppSnapshotLoader(mPersistInfoProvider))); + initialize(new TaskSnapshotCache(new AppSnapshotLoader(mPersistInfoProvider))); final boolean snapshotEnabled = !service.mContext .getResources() diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java index 817901f96ad4..fa2d9bf21dee 100644 --- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java +++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java @@ -322,15 +322,17 @@ public class TrustedPresentationListenerController { var listener = trustedPresentationInfo.mListener; boolean lastState = trustedPresentationInfo.mLastComputedTrustedPresentationState; boolean newState = - (alpha >= trustedPresentationInfo.mThresholds.minAlpha) && (fractionRendered - >= trustedPresentationInfo.mThresholds.minFractionRendered); + (alpha >= trustedPresentationInfo.mThresholds.getMinAlpha()) + && (fractionRendered >= trustedPresentationInfo.mThresholds + .getMinFractionRendered()); trustedPresentationInfo.mLastComputedTrustedPresentationState = newState; ProtoLog.v(WM_DEBUG_TPL, "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f " + "minFractionRendered=%f", - lastState, newState, alpha, trustedPresentationInfo.mThresholds.minAlpha, - fractionRendered, trustedPresentationInfo.mThresholds.minFractionRendered); + lastState, newState, alpha, trustedPresentationInfo.mThresholds.getMinAlpha(), + fractionRendered, trustedPresentationInfo.mThresholds + .getMinFractionRendered()); if (lastState && !newState) { // We were in the trusted presentation state, but now we left it, @@ -350,13 +352,15 @@ public class TrustedPresentationListenerController { trustedPresentationInfo.mEnteredTrustedPresentationStateTime = currTimeMs; mHandler.postDelayed(() -> { computeTpl(mLastWindowHandles); - }, (long) (trustedPresentationInfo.mThresholds.stabilityRequirementMs * 1.5)); + }, (long) (trustedPresentationInfo.mThresholds + .getStabilityRequirementMillis() * 1.5)); } // Has the timer elapsed, but we are still in the state? Emit a callback if needed if (!trustedPresentationInfo.mLastReportedTrustedPresentationState && newState && ( currTimeMs - trustedPresentationInfo.mEnteredTrustedPresentationStateTime - > trustedPresentationInfo.mThresholds.stabilityRequirementMs)) { + > trustedPresentationInfo.mThresholds + .getStabilityRequirementMillis())) { trustedPresentationInfo.mLastReportedTrustedPresentationState = true; addListenerUpdate(listenerUpdates, listener, trustedPresentationInfo.mId, /*presentationState*/ true); @@ -413,15 +417,6 @@ public class TrustedPresentationListenerController { mThresholds = thresholds; mId = id; mListener = listener; - checkValid(thresholds); - } - - private void checkValid(TrustedPresentationThresholds thresholds) { - if (thresholds.minAlpha <= 0 || thresholds.minFractionRendered <= 0 - || thresholds.stabilityRequirementMs < 1) { - throw new IllegalArgumentException( - "TrustedPresentationThresholds values are invalid"); - } } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index ae4c3b9a510a..669c61c4b40c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -1043,6 +1043,15 @@ public abstract class WindowManagerInternal { /** * Clears apps added to collection of apps in which screen capture should be disabled. * + * @param packageInfos set of {@link PackageInfo} whose windows should be unblocked + * from capture. + */ + public abstract void removeBlockScreenCaptureForApps( + @NonNull ArraySet<PackageInfo> packageInfos); + + /** + * Clears all apps added to collection of apps in which screen capture should be disabled. + * * <p> This clears and resets any existing set or added applications from * * {@link #addBlockScreenCaptureForApps(ArraySet)} */ diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 631ebcd2b6e9..3d6bd4fd4edd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -101,6 +101,7 @@ import static android.window.WindowProviderService.isWindowProviderService; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME; @@ -1344,7 +1345,7 @@ public class WindowManagerService extends IWindowManager.Stub LocalServices.addService(WindowManagerInternal.class, new LocalService()); LocalServices.addService( ImeTargetVisibilityPolicy.class, new ImeTargetVisibilityPolicyImpl()); - mEmbeddedWindowController = new EmbeddedWindowController(mAtmService); + mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager); mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( mContext.getResources()); @@ -8632,6 +8633,17 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void removeBlockScreenCaptureForApps(ArraySet<PackageInfo> packageInfos) { + synchronized (mGlobalLock) { + boolean modified = + mSensitiveContentPackages.removeBlockScreenCaptureForApps(packageInfos); + if (modified) { + WindowManagerService.this.refreshScreenCaptureDisabled(); + } + } + } + + @Override public void clearBlockedApps() { synchronized (mGlobalLock) { boolean modified = mSensitiveContentPackages.clearBlockedApps(); @@ -9044,73 +9056,37 @@ public class WindowManagerService extends IWindowManager.Stub null /* region */, clientToken); } - boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) { - final IBinder windowBinder = embeddedWindow.asBinder(); - final IBinder hostInputChannel, embeddedInputChannel; - synchronized (mGlobalLock) { - final EmbeddedWindowController.EmbeddedWindow ew = - mEmbeddedWindowController.getByWindowToken(windowBinder); - if (ew == null) { - Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window"); - return false; - } - final WindowState hostWindowState = ew.getWindowState(); - if (hostWindowState == null) { - Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" + - " associated host"); - return false; - } - embeddedInputChannel = ew.getInputChannelToken(); - if (embeddedInputChannel == null) { - Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" + - " channel"); - return false; - } - hostInputChannel = hostWindowState.mInputChannelToken; - if (hostInputChannel == null) { - Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" + - " input channel"); - return false; - } - return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel); + @Override + public boolean transferTouchGesture(InputTransferToken transferFromToken, + InputTransferToken transferToToken) { + if (transferFromToken == null || transferToToken == null) { + ProtoLog.e(WM_DEBUG_EMBEDDED_WINDOWS, + "transferTouchGesture failed because args transferFromToken or " + + "transferToToken is null"); + return false; } - } - - boolean transferHostTouchGestureToEmbedded(Session session, IWindow hostWindow, - InputTransferToken inputTransferToken) { - final IBinder hostInputChannel, embeddedInputChannel; - synchronized (mGlobalLock) { - final WindowState hostWindowState = windowForClientLocked(session, hostWindow, false); - if (hostWindowState == null) { - Slog.w(TAG, "Attempt to transfer touch gesture with invalid host window"); - return false; - } - final EmbeddedWindowController.EmbeddedWindow ew = - mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); - if (ew == null || ew.mHostWindowState == null) { - Slog.w(TAG, "Attempt to transfer touch gesture to non-existent embedded window"); - return false; - } - if (ew.mHostWindowState.mClient.asBinder() != hostWindow.asBinder()) { - Slog.w(TAG, "Attempt to transfer touch gesture to embedded window not associated" - + " with host window"); - return false; - } - embeddedInputChannel = ew.getInputChannelToken(); - if (embeddedInputChannel == null) { - Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" - + " channel"); - return false; - } - hostInputChannel = hostWindowState.mInputChannelToken; - if (hostInputChannel == null) { - Slog.w(TAG, - "Attempt to transfer touch focus to a host window with no input channel"); - return false; + final long identity = Binder.clearCallingIdentity(); + boolean didTransfer; + try { + synchronized (mGlobalLock) { + // If the transferToToken exists in the input to window map, it means the request + // is to transfer from embedded to host. Otherwise, the transferToToken + // represents an embedded window so transfer from host to embedded. + WindowState windowStateTo = mInputToWindowMap.get(transferToToken.mToken); + if (windowStateTo != null) { + didTransfer = mEmbeddedWindowController.transferToHost(transferFromToken, + windowStateTo); + } else { + WindowState windowStateFrom = mInputToWindowMap.get(transferFromToken.mToken); + didTransfer = mEmbeddedWindowController.transferToEmbedded(windowStateFrom, + transferToToken); + } } - return mInputManager.transferTouchFocus(hostInputChannel, embeddedInputChannel); + } finally { + Binder.restoreCallingIdentity(identity); } + return didTransfer; } private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f56e50e2e9fd..6e993b340352 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1897,7 +1897,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return true; } - if (com.android.server.notification.Flags.sensitiveNotificationAppProtection()) { + if (android.permission.flags.Flags.sensitiveNotificationAppProtection()) { if (mWmService.mSensitiveContentPackages .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) { return true; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 105dc880c9a7..0ccf810c720d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -102,6 +102,9 @@ final class DevicePolicyEngine { DevicePolicyIdentifiers.getIdentifierForUserRestriction( UserManager.DISALLOW_CELLULAR_2G); + //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit + private static final int DEFAULT_POLICY_SIZE_LIMIT = -1; + private final Context mContext; private final UserManager mUserManager; @@ -122,10 +125,11 @@ final class DevicePolicyEngine { * Map containing the current set of admins in each user with active policies. */ private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins; + private final SparseArray<HashMap<EnforcingAdmin, Integer>> mAdminPolicySize; - //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit - private static final int POLICY_SIZE_LIMIT = 99999; + private int mPolicySizeLimit = DEFAULT_POLICY_SIZE_LIMIT; + private final DeviceAdminServiceController mDeviceAdminServiceController; DevicePolicyEngine( @@ -1594,7 +1598,9 @@ final class DevicePolicyEngine { existingPolicySize = sizeOf(policyState.getPoliciesSetByAdmins().get(admin)); } int policySize = sizeOf(value); - if (currentAdminPoliciesSize + policySize - existingPolicySize < POLICY_SIZE_LIMIT) { + // Policy size limit is disabled if mPolicySizeLimit is -1. + if (mPolicySizeLimit == -1 + || currentAdminPoliciesSize + policySize - existingPolicySize < mPolicySizeLimit) { increasePolicySizeForAdmin( admin, /* policySizeDiff = */ policySize - existingPolicySize); return true; @@ -1642,6 +1648,26 @@ final class DevicePolicyEngine { } } + /** + * Updates the max allowed size limit for policies per admin. Setting it to -1, disables + * the limitation. + */ + void setMaxPolicyStorageLimit(int storageLimit) { + if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) { + throw new IllegalArgumentException("Can't set a size limit less than the minimum " + + "allowed size."); + } + mPolicySizeLimit = storageLimit; + } + + /** + * Returns the max allowed size limit for policies per admin. -1 means the limitation is + * disabled. + */ + int getMaxPolicyStorageLimit() { + return mPolicySizeLimit; + } + public void dump(IndentingPrintWriter pw) { synchronized (mLock) { pw.println("Local Policies: "); @@ -1761,6 +1787,7 @@ final class DevicePolicyEngine { private static final String TAG_ENFORCING_ADMIN_AND_SIZE = "enforcing-admin-and-size"; private static final String TAG_ENFORCING_ADMIN = "enforcing-admin"; private static final String TAG_POLICY_SUM_SIZE = "policy-sum-size"; + private static final String TAG_MAX_POLICY_SIZE_LIMIT = "max-policy-size-limit"; private static final String ATTR_USER_ID = "user-id"; private static final String ATTR_POLICY_SUM_SIZE = "size"; @@ -1805,6 +1832,7 @@ final class DevicePolicyEngine { writeGlobalPoliciesInner(serializer); writeEnforcingAdminsInner(serializer); writeEnforcingAdminSizeInner(serializer); + writeMaxPolicySizeInner(serializer); } private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException { @@ -1886,6 +1914,17 @@ final class DevicePolicyEngine { } } + private void writeMaxPolicySizeInner(TypedXmlSerializer serializer) + throws IOException { + if (!devicePolicySizeTrackingEnabled()) { + return; + } + serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT); + serializer.attributeInt( + /* namespace= */ null, ATTR_POLICY_SUM_SIZE, mPolicySizeLimit); + serializer.endTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT); + } + void readFromFileLocked() { if (!mFile.exists()) { Log.d(TAG, "" + mFile + " doesn't exist"); @@ -1926,6 +1965,9 @@ final class DevicePolicyEngine { case TAG_ENFORCING_ADMIN_AND_SIZE: readEnforcingAdminAndSizeInner(parser); break; + case TAG_MAX_POLICY_SIZE_LIMIT: + readMaxPolicySizeInner(parser); + break; default: Slogf.wtf(TAG, "Unknown tag " + tag); } @@ -2036,5 +2078,13 @@ final class DevicePolicyEngine { } mAdminPolicySize.get(admin.getUserId()).put(admin, size); } + + private void readMaxPolicySizeInner(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + if (!devicePolicySizeTrackingEnabled()) { + return; + } + mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b7a2271d8803..58e198e532cd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -28,10 +28,13 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUTOFILL; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_BLUETOOTH; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CALLS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION; @@ -50,6 +53,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE; @@ -60,6 +64,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PRINTING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS; @@ -73,6 +78,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING; @@ -85,6 +91,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WINDOWS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; import static android.Manifest.permission.MASTER_CLEAR; +import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE; import static android.Manifest.permission.QUERY_ADMIN_POLICY; import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY; import static android.Manifest.permission.SET_TIME; @@ -101,6 +108,7 @@ import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTI import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER; +import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK; import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA; import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED; @@ -235,6 +243,7 @@ import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled; import static android.app.admin.flags.Flags.headlessDeviceOwnerSingleUserEnabled; +import static android.app.admin.flags.Flags.permissionMigrationForZeroTrustImplEnabled; import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled; import static android.app.admin.flags.Flags.assistContentUserRestrictionEnabled; import static android.app.admin.flags.Flags.securityLogV2Enabled; @@ -324,6 +333,7 @@ import android.app.admin.DevicePolicyStringResource; import android.app.admin.DeviceStateCache; import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.FullyManagedDeviceProvisioningParams; +import android.app.admin.IAuditLogEventsCallback; import android.app.admin.IDevicePolicyManager; import android.app.admin.IntegerPolicyValue; import android.app.admin.IntentFilterPolicyKey; @@ -2056,7 +2066,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mLockPatternUtils = injector.newLockPatternUtils(); mLockSettingsInternal = injector.getLockSettingsInternal(); // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false? - mSecurityLogMonitor = new SecurityLogMonitor(this); + mSecurityLogMonitor = new SecurityLogMonitor(this, mHandler); mHasFeature = mInjector.hasFeature(); mIsWatch = mInjector.getPackageManager() @@ -2714,8 +2724,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private void maybeStartSecurityLogMonitorOnActivityManagerReady() { - synchronized (getLockObject()) { - if (mInjector.securityLogIsLoggingEnabled()) { + if (!mInjector.securityLogIsLoggingEnabled()) { + return; + } + + if (securityLogV2Enabled()) { + boolean auditLoggingEnabled = Boolean.TRUE.equals( + mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL)); + boolean securityLoggingEnabled = Boolean.TRUE.equals( + mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL)); + setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled); + } else { + synchronized (getLockObject()) { mSecurityLogMonitor.start(getSecurityLoggingEnabledUser()); mInjector.runCryptoSelfTest(); maybePauseDeviceWideLoggingLocked(); @@ -9524,7 +9546,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private int getHeadlessDeviceOwnerMode() { synchronized (getLockObject()) { - return getDeviceOwnerAdminLocked().info.getHeadlessDeviceOwnerMode(); + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + if (deviceOwner == null) { + return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; + } + return deviceOwner.info.getHeadlessDeviceOwnerMode(); } } @@ -12042,8 +12068,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (packageList != null) { - for (String pkg : packageList) { - PolicySizeVerifier.enforceMaxPackageNameLength(pkg); + if (!devicePolicySizeTrackingEnabled()) { + for (String pkg : packageList) { + PolicySizeVerifier.enforceMaxPackageNameLength(pkg); + } } List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() -> @@ -12279,17 +12307,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } final CallerIdentity caller = getCallerIdentity(admin); + // Only allow the system user to use this method + Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), + "createAndManageUser was called from non-system user"); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); + if (headlessDeviceOwnerSingleUserEnabled()) { // Block this method if the device is in headless main user mode Preconditions.checkCallAuthorization( getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER, "createAndManageUser was called while in headless single user mode"); } - // Only allow the system user to use this method - Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), - "createAndManageUser was called from non-system user"); - Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); - checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0; final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0 @@ -14362,8 +14391,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages) throws SecurityException { Objects.requireNonNull(packages, "packages is null"); - for (String pkg : packages) { - PolicySizeVerifier.enforceMaxPackageNameLength(pkg); + if (!devicePolicySizeTrackingEnabled()) { + for (String pkg : packages) { + PolicySizeVerifier.enforceMaxPackageNameLength(pkg); + } } CallerIdentity caller = getCallerIdentity(who, callerPackageName); @@ -15767,7 +15798,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void enforceSecurityLoggingPolicy(boolean enabled) { - enforceLoggingPolicy(enabled); + if (!securityLogV2Enabled()) { + return; + } + Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL); + enforceLoggingPolicy(enabled, Boolean.TRUE.equals(auditLoggingEnabled)); + } + + @Override + public void enforceAuditLoggingPolicy(boolean enabled) { + if (!securityLogV2Enabled()) { + return; + } + Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL); + enforceLoggingPolicy(Boolean.TRUE.equals(securityLoggingEnabled), enabled); } private List<EnforcingUser> getEnforcingUsers(Set<EnforcingAdmin> admins) { @@ -15787,17 +15833,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void enforceLoggingPolicy(boolean securityLoggingEnabled) { - Slogf.i(LOG_TAG, "Enforcing security logging, securityLoggingEnabled: %b", - securityLoggingEnabled); - SecurityLog.setLoggingEnabledProperty(securityLoggingEnabled); - if (securityLoggingEnabled) { - mSecurityLogMonitor.start(getSecurityLoggingEnabledUser()); + private void enforceLoggingPolicy( + boolean securityLoggingEnabled, boolean auditLoggingEnabled) { + Slogf.i(LOG_TAG, "Enforcing logging policy, security: %b audit: %b", + securityLoggingEnabled, auditLoggingEnabled); + SecurityLog.setLoggingEnabledProperty(securityLoggingEnabled || auditLoggingEnabled); + setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled); + } + + private void setLoggingConfiguration( + boolean securityLoggingEnabled, boolean auditLoggingEnabled) { + final int loggingEnabledUser = getSecurityLoggingEnabledUser(); + mSecurityLogMonitor.setLoggingParams( + loggingEnabledUser, securityLoggingEnabled, auditLoggingEnabled); + if (securityLoggingEnabled || auditLoggingEnabled) { synchronized (getLockObject()) { maybePauseDeviceWideLoggingLocked(); } - } else { - mSecurityLogMonitor.stop(); } } @@ -16243,7 +16295,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void notifyPendingSystemUpdate(@Nullable SystemUpdateInfo info) { Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE), + hasCallingOrSelfPermission(NOTIFY_PENDING_SYSTEM_UPDATE), "Only the system update service can broadcast update information"); mInjector.binderWithCleanCallingIdentity(() -> { @@ -16284,12 +16336,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Send broadcasts to corresponding profile owners if any. for (final int userId : runningUserIds) { + final ComponentName profileOwnerPackage; synchronized (getLockObject()) { - final ComponentName profileOwnerPackage = - mOwners.getProfileOwnerComponent(userId); - if (profileOwnerPackage != null) { - intent.setComponent(profileOwnerPackage); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + profileOwnerPackage = mOwners.getProfileOwnerComponent(userId); + } + if (profileOwnerPackage != null) { + intent.setComponent(profileOwnerPackage); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + } + + if (permissionMigrationForZeroTrustImplEnabled()) { + final UserHandle user = UserHandle.of(userId); + final String roleHolderPackage = getRoleHolderPackageNameOnUser( + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId); + if (roleHolderPackage != null) { + broadcastExplicitIntentToPackage(intent, roleHolderPackage, user); } } } @@ -16297,13 +16358,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) { - Objects.requireNonNull(admin, "ComponentName is null"); - - final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwner(caller)); + public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) { + if (permissionMigrationForZeroTrustImplEnabled()) { + CallerIdentity caller = getCallerIdentity(admin, callerPackage); + enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE, + MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(), + caller.getUserId()); + } else { + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(admin); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); + } return mOwners.getSystemUpdateInfo(); } @@ -17863,6 +17930,82 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void setAuditLogEnabled(String callingPackage, boolean enabled) { + if (!mHasFeature) { + return; + } + final CallerIdentity caller = getCallerIdentity(callingPackage); + + if (!securityLogV2Enabled()) { + throw new UnsupportedOperationException("Audit log not enabled"); + } + + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + null /* admin */, + MANAGE_DEVICE_POLICY_AUDIT_LOGGING, + caller.getPackageName(), + caller.getUserId()); + if (enabled) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.AUDIT_LOGGING, + admin, + new BooleanPolicyValue(true)); + } else { + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.AUDIT_LOGGING, + admin); + mSecurityLogMonitor.setAuditLogEventsCallback(caller.getUid(), null /* callback */); + } + } + + @Override + public boolean isAuditLogEnabled(String callingPackage) { + if (!mHasFeature) { + return false; + } + + if (!securityLogV2Enabled()) { + throw new UnsupportedOperationException("Audit log not enabled"); + } + + final CallerIdentity caller = getCallerIdentity(callingPackage); + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + null /* admin */, + MANAGE_DEVICE_POLICY_AUDIT_LOGGING, + caller.getPackageName(), + caller.getUserId()); + + Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin( + PolicyDefinition.AUDIT_LOGGING, admin); + + return Boolean.TRUE.equals(policy); + } + + @Override + public void setAuditLogEventsCallback(String callingPackage, IAuditLogEventsCallback callback) { + if (!mHasFeature) { + return; + } + + final CallerIdentity caller = getCallerIdentity(callingPackage); + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + null /* admin */, + MANAGE_DEVICE_POLICY_AUDIT_LOGGING, + caller.getPackageName(), + caller.getUserId()); + + Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin( + PolicyDefinition.AUDIT_LOGGING, admin); + + if (!Boolean.TRUE.equals(policy)) { + throw new IllegalStateException( + "Managing app has to enable audit log before setting events callback"); + } + + mSecurityLogMonitor.setAuditLogEventsCallback(caller.getUid(), callback); + } + + @Override public long forceSecurityLogs() { Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS), @@ -20807,14 +20950,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } final CallerIdentity caller = getCallerIdentity(callerPackage); - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + if (permissionMigrationForZeroTrustImplEnabled()) { + enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName()); + } else { + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller) + || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + } synchronized (getLockObject()) { final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( caller.getUserId()); - final String esid = requiredAdmin.mEnrollmentSpecificId; + final String esid = requiredAdmin != null ? requiredAdmin.mEnrollmentSpecificId : null; return esid != null ? esid : ""; } } @@ -21952,6 +22099,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public boolean isTheftDetectionTriggered(String callerPackageName) { + final CallerIdentity caller = getCallerIdentity(callerPackageName); + if (!android.app.admin.flags.Flags.deviceTheftImplEnabled()) { + return false; + } + enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(), + caller.getUserId()); + + //STOPSHIP: replace 1<<9 with + // LockPatternUtils.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST once ag/26042068 lands + return 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId()) & (1 << 9)); + } + + @Override public void setWifiSsidPolicy(String callerPackageName, WifiSsidPolicy policy) { CallerIdentity caller; @@ -22392,14 +22553,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } - // Permission that will need to be created in V. - private static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = - "manage_device_policy_block_uninstall"; - private static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = - "manage_device_policy_camera_toggle"; - private static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = - "manage_device_policy_microphone_toggle"; - // DPC types private static final int NOT_A_DPC = -1; private static final int DEFAULT_DEVICE_OWNER = 0; @@ -22485,7 +22638,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_WINDOWS, MANAGE_DEVICE_POLICY_WIPE_DATA, SET_TIME, - SET_TIME_ZONE + SET_TIME_ZONE, + MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES ); private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of( MANAGE_DEVICE_POLICY_ACROSS_USERS, @@ -22549,7 +22703,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_TIME, MANAGE_DEVICE_POLICY_VPN, - MANAGE_DEVICE_POLICY_WIPE_DATA + MANAGE_DEVICE_POLICY_WIPE_DATA, + MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES ); /** @@ -24075,5 +24230,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } return adminOwnedSubscriptions; }); + + } + + @Override + public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) { + if (!devicePolicySizeTrackingEnabled()) { + return; + } + CallerIdentity caller = getCallerIdentity(callerPackageName); + enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(), + caller.getUserId()); + + mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit); + } + + @Override + public int getMaxPolicyStorageLimit(String callerPackageName) { + if (!devicePolicySizeTrackingEnabled()) { + return -1; + } + CallerIdentity caller = getCallerIdentity(callerPackageName); + enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(), + caller.getUserId()); + + return mDevicePolicyEngine.getMaxPolicyStorageLimit(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index 3474db3c7c1f..1247f900260a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -141,6 +141,13 @@ final class PolicyDefinition<V> { PolicyEnforcerCallbacks::enforceSecurityLogging, new BooleanPolicySerializer()); + static PolicyDefinition<Boolean> AUDIT_LOGGING = new PolicyDefinition<>( + new NoArgsPolicyKey(DevicePolicyIdentifiers.AUDIT_LOGGING_POLICY), + TRUE_MORE_RESTRICTIVE, + POLICY_FLAG_GLOBAL_ONLY_POLICY, + PolicyEnforcerCallbacks::enforceAuditLogging, + new BooleanPolicySerializer()); + static PolicyDefinition<LockTaskPolicy> LOCK_TASK = new PolicyDefinition<>( new NoArgsPolicyKey(DevicePolicyIdentifiers.LOCK_TASK_POLICY), new TopPriority<>(List.of( @@ -365,6 +372,8 @@ final class PolicyDefinition<V> { GENERIC_PERMISSION_GRANT); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY, SECURITY_LOGGING); + POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUDIT_LOGGING_POLICY, + AUDIT_LOGGING); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY, USER_CONTROLLED_DISABLED_PACKAGES); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index 4aaefa670ea2..54242ab279b0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -136,6 +136,14 @@ final class PolicyEnforcerCallbacks { return true; } + static boolean enforceAuditLogging( + @Nullable Boolean value, @NonNull Context context, int userId, + @NonNull PolicyKey policyKey) { + final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); + dpmi.enforceAuditLoggingPolicy(Boolean.TRUE.equals(value)); + return true; + } + static boolean setLockTask( @Nullable LockTaskPolicy policy, @NonNull Context context, int userId) { List<String> packages = Collections.emptyList(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java index 7a4454b11fce..02f39189ae72 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java @@ -16,22 +16,32 @@ package com.android.server.devicepolicy; +import static android.app.admin.flags.Flags.securityLogV2Enabled; + import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import android.app.admin.DeviceAdminReceiver; +import android.app.admin.IAuditLogEventsCallback; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; +import android.os.Handler; +import android.os.IBinder; import android.os.Process; +import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.utils.Slogf; import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -53,15 +63,11 @@ class SecurityLogMonitor implements Runnable { private int mEnabledUser; - SecurityLogMonitor(DevicePolicyManagerService service) { - this(service, 0 /* id */); - } - - @VisibleForTesting - SecurityLogMonitor(DevicePolicyManagerService service, long id) { + SecurityLogMonitor(DevicePolicyManagerService service, Handler handler) { mService = service; - mId = id; + mId = 0; mLastForceNanos = System.nanoTime(); + mHandler = handler; } private static final boolean DEBUG = false; // STOPSHIP if true. @@ -118,6 +124,9 @@ class SecurityLogMonitor implements Runnable { @GuardedBy("mLock") private boolean mCriticalLevelLogged = false; + private boolean mLegacyLogEnabled; + private boolean mAuditLogEnabled; + /** * Last events fetched from log to check for overlap between batches. We can leave it empty if * we are sure there will be no overlap anymore, e.g. when we get empty batch. @@ -143,6 +152,40 @@ class SecurityLogMonitor implements Runnable { private long mLastForceNanos = 0; /** + * Handler shared with DPMS. + */ + private final Handler mHandler; + + /** + * Oldest events get purged from audit log buffer if total number exceeds this value. + */ + private static final int MAX_AUDIT_LOG_EVENTS = 10000; + /** + * Events older than this get purged from audit log buffer. + */ + private static final long MAX_AUDIT_LOG_EVENT_AGE_NS = TimeUnit.HOURS.toNanos(8); + + /** + * Audit log callbacks keyed by UID. The code should maintain the following invariant: all + * callbacks in this map have received (or are scheduled to receive) all events in + * mAuditLogEventsBuffer. To ensure this, before a callback is put into this map, it must be + * scheduled to receive all the events in the buffer, and conversely, before a new chunk of + * events is added to the buffer, it must be scheduled to be sent to all callbacks already in + * this list. All scheduling should happen on mHandler, so that they aren't reordered, and + * while holding the lock. This ensures that no callback misses an event or receives a duplicate + * or out of order events. + */ + @GuardedBy("mLock") + private final SparseArray<IAuditLogEventsCallback> mAuditLogCallbacks = new SparseArray<>(); + + /** + * Audit log event buffer. It is shrunk automatically whenever either there are too many events + * or the oldest one is too old. + */ + @GuardedBy("mLock") + private final ArrayDeque<SecurityEvent> mAuditLogEventBuffer = new ArrayDeque<>(); + + /** * Start security logging. * * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all @@ -154,18 +197,8 @@ class SecurityLogMonitor implements Runnable { mLock.lock(); try { if (mMonitorThread == null) { - mPendingLogs = new ArrayList<>(); - mCriticalLevelLogged = false; - mId = 0; - mAllowedToRetrieve = false; - mNextAllowedRetrievalTimeMillis = -1; - mPaused = false; - - mMonitorThread = new Thread(this); - mMonitorThread.start(); - - SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED); - Slog.i(TAG, "Security log monitor thread started"); + resetLegacyBufferLocked(); + startMonitorThreadLocked(); } else { Slog.i(TAG, "Security log monitor thread is already running"); } @@ -176,29 +209,82 @@ class SecurityLogMonitor implements Runnable { void stop() { Slog.i(TAG, "Stopping security logging."); - SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED); mLock.lock(); try { if (mMonitorThread != null) { - mMonitorThread.interrupt(); - try { - mMonitorThread.join(TimeUnit.SECONDS.toMillis(5)); - } catch (InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for thread to stop", e); - } - // Reset state and clear buffer - mPendingLogs = new ArrayList<>(); - mId = 0; - mAllowedToRetrieve = false; - mNextAllowedRetrievalTimeMillis = -1; - mPaused = false; - mMonitorThread = null; + stopMonitorThreadLocked(); + resetLegacyBufferLocked(); } } finally { mLock.unlock(); } } + void setLoggingParams(int enabledUser, boolean legacyLogEnabled, boolean auditLogEnabled) { + Slogf.i(TAG, "Setting logging params, user = %d -> %d, legacy: %b -> %b, audit %b -> %b", + mEnabledUser, enabledUser, mLegacyLogEnabled, legacyLogEnabled, mAuditLogEnabled, + auditLogEnabled); + mLock.lock(); + try { + mEnabledUser = enabledUser; + if (mMonitorThread == null && (legacyLogEnabled || auditLogEnabled)) { + startMonitorThreadLocked(); + } else if (mMonitorThread != null && !legacyLogEnabled && !auditLogEnabled) { + stopMonitorThreadLocked(); + } + + if (mLegacyLogEnabled != legacyLogEnabled) { + resetLegacyBufferLocked(); + mLegacyLogEnabled = legacyLogEnabled; + } + + if (mAuditLogEnabled != auditLogEnabled) { + resetAuditBufferLocked(); + mAuditLogEnabled = auditLogEnabled; + } + } finally { + mLock.unlock(); + } + + } + + @GuardedBy("mLock") + private void startMonitorThreadLocked() { + mId = 0; + mPaused = false; + mMonitorThread = new Thread(this); + mMonitorThread.start(); + SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED); + Slog.i(TAG, "Security log monitor thread started"); + } + + @GuardedBy("mLock") + private void stopMonitorThreadLocked() { + mMonitorThread.interrupt(); + try { + mMonitorThread.join(TimeUnit.SECONDS.toMillis(5)); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for thread to stop", e); + } + mMonitorThread = null; + SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED); + } + + @GuardedBy("mLock") + private void resetLegacyBufferLocked() { + mPendingLogs = new ArrayList<>(); + mCriticalLevelLogged = false; + mAllowedToRetrieve = false; + mNextAllowedRetrievalTimeMillis = -1; + Slog.i(TAG, "Legacy buffer reset."); + } + + @GuardedBy("mLock") + private void resetAuditBufferLocked() { + mAuditLogEventBuffer.clear(); + mAuditLogCallbacks.clear(); + } + /** * If logs are being collected, keep collecting them but stop notifying the device owner that * new logs are available (since they cannot be retrieved). @@ -338,8 +424,7 @@ class SecurityLogMonitor implements Runnable { */ @GuardedBy("mLock") private void mergeBatchLocked(final ArrayList<SecurityEvent> newLogs) { - // Reserve capacity so that copying doesn't occur. - mPendingLogs.ensureCapacity(mPendingLogs.size() + newLogs.size()); + List<SecurityEvent> dedupedLogs = new ArrayList<>(); // Run through the first events of the batch to check if there is an overlap with previous // batch and if so, skip overlapping events. Events are sorted by timestamp, so we can // compare it in linear time by advancing two pointers, one for each batch. @@ -358,8 +443,7 @@ class SecurityLogMonitor implements Runnable { if (lastNanos > currentNanos) { // New event older than the last we've seen so far, must be due to reordering. if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos); - assignLogId(curEvent); - mPendingLogs.add(curEvent); + dedupedLogs.add(curEvent); curPos++; } else if (lastNanos < currentNanos) { if (DEBUG) Slog.d(TAG, "Event disappeared from the overlap: " + lastNanos); @@ -371,8 +455,7 @@ class SecurityLogMonitor implements Runnable { if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos); } else { // Wow, what a coincidence, or probably the clock is too coarse. - assignLogId(curEvent); - mPendingLogs.add(curEvent); + dedupedLogs.add(curEvent); if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos); } lastPos++; @@ -380,12 +463,23 @@ class SecurityLogMonitor implements Runnable { } } // Assign an id to the new logs, after the overlap with mLastEvents. - List<SecurityEvent> idLogs = newLogs.subList(curPos, newLogs.size()); - for (SecurityEvent event : idLogs) { + dedupedLogs.addAll(newLogs.subList(curPos, newLogs.size())); + for (SecurityEvent event : dedupedLogs) { assignLogId(event); } + + if (!securityLogV2Enabled() || mLegacyLogEnabled) { + addToLegacyBuffer(dedupedLogs); + } + + if (securityLogV2Enabled() && mAuditLogEnabled) { + addAuditLogEvents(dedupedLogs); + } + } + + private void addToLegacyBuffer(List<SecurityEvent> dedupedLogs) { // Save the rest of the new batch. - mPendingLogs.addAll(idLogs); + mPendingLogs.addAll(dedupedLogs); checkCriticalLevel(); @@ -453,7 +547,10 @@ class SecurityLogMonitor implements Runnable { saveLastEvents(newLogs); newLogs.clear(); - notifyDeviceOwnerOrProfileOwnerIfNeeded(force); + + if (!securityLogV2Enabled() || mLegacyLogEnabled) { + notifyDeviceOwnerOrProfileOwnerIfNeeded(force); + } } catch (IOException e) { Log.e(TAG, "Failed to read security log", e); } catch (InterruptedException e) { @@ -532,4 +629,121 @@ class SecurityLogMonitor implements Runnable { return 0; } } + + public void setAuditLogEventsCallback(int uid, IAuditLogEventsCallback callback) { + mLock.lock(); + try { + if (callback == null) { + mAuditLogCallbacks.remove(uid); + Slogf.i(TAG, "Cleared audit log callback for UID %d", uid); + return; + } + // Create a copy while holding the lock, so that that new events are not added + // resulting in duplicates. + final List<SecurityEvent> events = new ArrayList<>(mAuditLogEventBuffer); + scheduleSendAuditLogs(uid, callback, events); + mAuditLogCallbacks.append(uid, callback); + } finally { + mLock.unlock(); + } + Slogf.i(TAG, "Set audit log callback for UID %d", uid); + } + + private void addAuditLogEvents(List<SecurityEvent> events) { + mLock.lock(); + try { + if (mPaused) { + // TODO: maybe we need to stash the logs in some temp buffer wile paused so that + // they can be accessed after affiliation is fixed. + return; + } + if (!events.isEmpty()) { + for (int i = 0; i < mAuditLogCallbacks.size(); i++) { + final int uid = mAuditLogCallbacks.keyAt(i); + scheduleSendAuditLogs(uid, mAuditLogCallbacks.valueAt(i), events); + } + } + if (DEBUG) { + Slogf.d(TAG, "Adding audit %d events to % already present in the buffer", + events.size(), mAuditLogEventBuffer.size()); + } + mAuditLogEventBuffer.addAll(events); + trimAuditLogBufferLocked(); + if (DEBUG) { + Slogf.d(TAG, "Audit event buffer size after trimming: %d", + mAuditLogEventBuffer.size()); + } + } finally { + mLock.unlock(); + } + } + + @GuardedBy("mLock") + private void trimAuditLogBufferLocked() { + long nowNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); + + final Iterator<SecurityEvent> iterator = mAuditLogEventBuffer.iterator(); + while (iterator.hasNext()) { + final SecurityEvent event = iterator.next(); + if (mAuditLogEventBuffer.size() <= MAX_AUDIT_LOG_EVENTS + && nowNanos - event.getTimeNanos() <= MAX_AUDIT_LOG_EVENT_AGE_NS) { + break; + } + + iterator.remove(); + } + } + + private void scheduleSendAuditLogs( + int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) { + if (DEBUG) { + Slogf.d(TAG, "Scheduling to send %d audit log events to UID %d", events.size(), uid); + } + mHandler.post(() -> sendAuditLogs(uid, callback, events)); + } + + private void sendAuditLogs( + int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) { + try { + final int size = events.size(); + if (DEBUG) { + Slogf.d(TAG, "Sending %d audit log events to UID %d", size, uid); + } + callback.onNewAuditLogEvents(events); + if (DEBUG) { + Slogf.d(TAG, "Sent %d audit log events to UID %d", size, uid); + } + } catch (RemoteException e) { + Slogf.e(TAG, e, "Failed to invoke audit log callback for UID %d", uid); + removeAuditLogEventsCallbackIfDead(uid, callback); + } + } + + private void removeAuditLogEventsCallbackIfDead(int uid, IAuditLogEventsCallback callback) { + final IBinder binder = callback.asBinder(); + if (binder.isBinderAlive()) { + Slog.i(TAG, "Callback binder is still alive, not removing."); + return; + } + + mLock.lock(); + try { + int index = mAuditLogCallbacks.indexOfKey(uid); + if (index < 0) { + Slogf.i(TAG, "Callback not registered for UID %d, nothing to remove", uid); + return; + } + + final IBinder storedBinder = mAuditLogCallbacks.valueAt(index).asBinder(); + if (!storedBinder.equals(binder)) { + Slogf.i(TAG, "Callback is already replaced for UID %d, not removing", uid); + return; + } + + Slogf.i(TAG, "Removing callback for UID %d", uid); + mAuditLogCallbacks.removeAt(index); + } finally { + mLock.unlock(); + } + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 1d89d1722f74..ee758dbd0516 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -107,6 +107,7 @@ import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockSettingsInternal; +import com.android.server.adaptiveauth.AdaptiveAuthService; import com.android.server.am.ActivityManagerService; import com.android.server.appbinding.AppBindingService; import com.android.server.appop.AppOpMigrationHelper; @@ -2615,6 +2616,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(AuthService.class); t.traceEnd(); + if (android.adaptiveauth.Flags.enableAdaptiveAuth()) { + t.traceBegin("StartAdaptiveAuthService"); + mSystemServiceManager.startService(AdaptiveAuthService.class); + t.traceEnd(); + } + if (!isWatch) { // We don't run this on watches as there are no plans to use the data logged // on watch devices. @@ -3014,7 +3021,7 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - if (com.android.server.notification.Flags.sensitiveNotificationAppProtection() + if (android.permission.flags.Flags.sensitiveNotificationAppProtection() || android.view.flags.Flags.sensitiveContentAppProtection()) { t.traceBegin("StartSensitiveContentProtectionManager"); mSystemServiceManager.startService(SensitiveContentProtectionManagerService.class); 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 558827631dfe..cb3ee7307e36 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 @@ -46,6 +46,7 @@ import com.android.server.pm.KnownPackages import com.android.server.pm.parsing.PackageInfoUtils import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState +import libcore.util.EmptyArray class AppIdPermissionPolicy : SchemePolicy() { private val persistence = AppIdPermissionPersistence() @@ -73,40 +74,42 @@ class AppIdPermissionPolicy : SchemePolicy() { } override fun MutateStateScope.onInitialized() { - newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) -> - val oldPermission = newState.systemState.permissions[permissionName] - val newPermission = - if (oldPermission != null) { - if (permissionEntry.gids != null) { - oldPermission.copy( - gids = permissionEntry.gids, - areGidsPerUser = permissionEntry.perUser - ) - } else { - return@forEach - } - } else { - @Suppress("DEPRECATION") - val permissionInfo = - PermissionInfo().apply { - name = permissionName - packageName = PLATFORM_PACKAGE_NAME - protectionLevel = PermissionInfo.PROTECTION_SIGNATURE + if (!Flags.newPermissionGidEnabled()) { + newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) -> + val oldPermission = newState.systemState.permissions[permissionName] + val newPermission = + if (oldPermission != null) { + if (permissionEntry.gids != null) { + oldPermission.copy( + gids = permissionEntry.gids, + areGidsPerUser = permissionEntry.perUser + ) + } else { + return@forEach } - if (permissionEntry.gids != null) { - Permission( - permissionInfo, - false, - Permission.TYPE_CONFIG, - 0, - permissionEntry.gids, - permissionEntry.perUser - ) } else { - Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0) + @Suppress("DEPRECATION") + val permissionInfo = + PermissionInfo().apply { + name = permissionName + packageName = PLATFORM_PACKAGE_NAME + protectionLevel = PermissionInfo.PROTECTION_SIGNATURE + } + if (permissionEntry.gids != null) { + Permission( + permissionInfo, + false, + Permission.TYPE_CONFIG, + 0, + permissionEntry.gids, + permissionEntry.perUser + ) + } else { + Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0) + } } - } - newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission + newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission + } } } @@ -459,7 +462,7 @@ class AppIdPermissionPolicy : SchemePolicy() { ) return@forEachIndexed } - val newPermission = + var newPermission = if (oldPermission != null && newPackageName != oldPermission.packageName) { val oldPackageName = oldPermission.packageName // Only allow system apps to redefine non-system permissions. @@ -582,6 +585,24 @@ class AppIdPermissionPolicy : SchemePolicy() { ) } } + if (Flags.newPermissionGidEnabled()) { + var gids = EmptyArray.INT + var areGidsPerUser = false + if (!parsedPermission.isTree && packageState.isSystem) { + newState.externalState.configPermissions[permissionName]?.let { + gids = it.gids + areGidsPerUser = it.perUser + } + } + newPermission = Permission( + newPermissionInfo, + true, + Permission.TYPE_MANIFEST, + packageState.appId, + gids, + areGidsPerUser + ) + } if (parsedPermission.isTree) { newState.mutateSystemState().mutatePermissionTrees()[permissionName] = newPermission diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 67f66de71d39..0704c8ffca25 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -466,7 +466,7 @@ class PermissionService(private val service: AccessCheckingService) : return size } - override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int { + override fun checkUidPermission(uid: Int, permissionName: String, deviceId: String): Int { val userId = UserHandle.getUserId(uid) if (!userManagerInternal.exists(userId)) { return PackageManager.PERMISSION_DENIED @@ -489,15 +489,9 @@ class PermissionService(private val service: AccessCheckingService) : return PackageManager.PERMISSION_DENIED } - val persistentDeviceId = getPersistentDeviceId(deviceId) - if (persistentDeviceId == null) { - Slog.e(LOG_TAG, "Cannot find persistent device id for $deviceId.") - return PackageManager.PERMISSION_DENIED - } - val isPermissionGranted = service.getState { - isPermissionGranted(packageState, userId, permissionName, persistentDeviceId) + isPermissionGranted(packageState, userId, permissionName, deviceId) } return if (isPermissionGranted) { PackageManager.PERMISSION_GRANTED @@ -531,7 +525,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun checkPermission( packageName: String, permissionName: String, - persistentDeviceId: String, + deviceId: String, userId: Int ): Int { if (!userManagerInternal.exists(userId)) { @@ -545,9 +539,7 @@ class PermissionService(private val service: AccessCheckingService) : ?: return PackageManager.PERMISSION_DENIED val isPermissionGranted = - service.getState { - isPermissionGranted(packageState, userId, permissionName, persistentDeviceId) - } + service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) } return if (isPermissionGranted) { PackageManager.PERMISSION_GRANTED } else { @@ -565,21 +557,13 @@ class PermissionService(private val service: AccessCheckingService) : packageState: PackageState, userId: Int, permissionName: String, - persistentDeviceId: String + deviceId: String ): Boolean { val appId = packageState.appId // Note that instant apps can't have shared UIDs, so we only need to check the current // package state. val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp - if ( - isSinglePermissionGranted( - appId, - userId, - isInstantApp, - permissionName, - persistentDeviceId - ) - ) { + if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) { return true } @@ -591,7 +575,7 @@ class PermissionService(private val service: AccessCheckingService) : userId, isInstantApp, fullerPermissionName, - persistentDeviceId + deviceId ) ) { return true @@ -606,9 +590,9 @@ class PermissionService(private val service: AccessCheckingService) : userId: Int, isInstantApp: Boolean, permissionName: String, - persistentDeviceId: String, + deviceId: String, ): Boolean { - val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) + val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) if (!PermissionFlags.isPermissionGranted(flags)) { return false } @@ -689,22 +673,16 @@ class PermissionService(private val service: AccessCheckingService) : override fun grantRuntimePermission( packageName: String, permissionName: String, - persistentDeviceId: String, + deviceId: String, userId: Int ) { - setRuntimePermissionGranted( - packageName, - userId, - permissionName, - persistentDeviceId, - isGranted = true - ) + setRuntimePermissionGranted(packageName, userId, permissionName, deviceId, isGranted = true) } override fun revokeRuntimePermission( packageName: String, permissionName: String, - persistentDeviceId: String, + deviceId: String, userId: Int, reason: String? ) { @@ -712,7 +690,7 @@ class PermissionService(private val service: AccessCheckingService) : packageName, userId, permissionName, - persistentDeviceId, + deviceId, isGranted = false, revokeReason = reason ) @@ -740,7 +718,7 @@ class PermissionService(private val service: AccessCheckingService) : packageName: String, userId: Int, permissionName: String, - persistentDeviceId: String, + deviceId: String, isGranted: Boolean, skipKillUid: Boolean = false, revokeReason: String? = null @@ -765,7 +743,7 @@ class PermissionService(private val service: AccessCheckingService) : (if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") + ", userId = $userId," + " callingUid = $callingUidName ($callingUid))," + - " persistentDeviceId = $persistentDeviceId", + " deviceId = $deviceId", RuntimeException() ) } @@ -835,7 +813,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState, userId, permissionName, - persistentDeviceId, + deviceId, isGranted, canManageRolePermission, overridePolicyFixed, @@ -923,7 +901,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState: PackageState, userId: Int, permissionName: String, - persistentDeviceId: String, + deviceId: String, isGranted: Boolean, canManageRolePermission: Boolean, overridePolicyFixed: Boolean, @@ -982,8 +960,7 @@ class PermissionService(private val service: AccessCheckingService) : } val appId = packageState.appId - val oldFlags = - getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) + val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) { if (reportError) { @@ -1055,7 +1032,7 @@ class PermissionService(private val service: AccessCheckingService) : return } - setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags) + setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags) if (permission.isRuntime) { val action = @@ -1089,7 +1066,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun getPermissionFlags( packageName: String, permissionName: String, - persistentDeviceId: String, + deviceId: String, userId: Int, ): Int { if (!userManagerInternal.exists(userId)) { @@ -1125,12 +1102,7 @@ class PermissionService(private val service: AccessCheckingService) : } val flags = - getPermissionFlagsWithPolicy( - packageState.appId, - userId, - permissionName, - persistentDeviceId - ) + getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId) return PermissionFlags.toApiFlags(flags) } @@ -1138,7 +1110,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun getAllPermissionStates( packageName: String, - persistentDeviceId: String, + deviceId: String, userId: Int ): Map<String, PermissionState> { if (!userManagerInternal.exists(userId)) { @@ -1165,14 +1137,15 @@ class PermissionService(private val service: AccessCheckingService) : val permissionFlagsMap = service.getState { - if (persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) { + if (deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) { with(policy) { getAllPermissionFlags(packageState.appId, userId) } } else { with(devicePolicy) { - getAllPermissionFlags(packageState.appId, persistentDeviceId, userId) + getAllPermissionFlags(packageState.appId, deviceId, userId) } } - } ?: return emptyMap() + } + ?: return emptyMap() val permissionStates = ArrayMap<String, PermissionState>() permissionFlagsMap.forEachIndexed { _, permissionName, flags -> @@ -1186,7 +1159,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun isPermissionRevokedByPolicy( packageName: String, permissionName: String, - deviceId: Int, + deviceId: String, userId: Int ): Boolean { if (!userManagerInternal.exists(userId)) { @@ -1207,24 +1180,13 @@ class PermissionService(private val service: AccessCheckingService) : } ?: return false - val persistentDeviceId = getPersistentDeviceId(deviceId) - if (persistentDeviceId == null) { - Slog.w(LOG_TAG, "Cannot find persistent device Id for $deviceId") - return false - } - service.getState { - if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) { + if (isPermissionGranted(packageState, userId, permissionName, deviceId)) { return false } val flags = - getPermissionFlagsWithPolicy( - packageState.appId, - userId, - permissionName, - persistentDeviceId - ) + getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId) return flags.hasBits(PermissionFlags.POLICY_FIXED) } @@ -1248,7 +1210,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun shouldShowRequestPermissionRationale( packageName: String, permissionName: String, - deviceId: Int, + deviceId: String, userId: Int, ): Boolean { if (!userManagerInternal.exists(userId)) { @@ -1274,19 +1236,13 @@ class PermissionService(private val service: AccessCheckingService) : return false } - val persistentDeviceId = getPersistentDeviceId(deviceId) - if (persistentDeviceId == null) { - Slog.w(LOG_TAG, "Cannot find persistent device Id for $deviceId") - return false - } - val flags: Int service.getState { - if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) { + if (isPermissionGranted(packageState, userId, permissionName, deviceId)) { return false } - flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) + flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) } if (flags.hasAnyBit(UNREQUESTABLE_MASK)) { return false @@ -1325,7 +1281,7 @@ class PermissionService(private val service: AccessCheckingService) : flagMask: Int, flagValues: Int, enforceAdjustPolicyPermission: Boolean, - persistentDeviceId: String, + deviceId: String, userId: Int ) { val callingUid = Binder.getCallingUid() @@ -1351,7 +1307,7 @@ class PermissionService(private val service: AccessCheckingService) : "updatePermissionFlags(packageName = $packageName," + " permissionName = $permissionName, flagMask = $flagMaskString," + " flagValues = $flagValuesString, userId = $userId," + - " persistentDeviceId = $persistentDeviceId," + + " deviceId = $deviceId," + " callingUid = $callingUidName ($callingUid))", RuntimeException() ) @@ -1441,7 +1397,7 @@ class PermissionService(private val service: AccessCheckingService) : appId, userId, permissionName, - persistentDeviceId, + deviceId, flagMask, flagValues, canUpdateSystemFlags, @@ -1527,7 +1483,7 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - persistentDeviceId: String, + deviceId: String, flagMask: Int, flagValues: Int, canUpdateSystemFlags: Boolean, @@ -1561,8 +1517,7 @@ class PermissionService(private val service: AccessCheckingService) : return } - val oldFlags = - getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) + val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) if (!isPermissionRequested && oldFlags == 0) { Slog.w( LOG_TAG, @@ -1573,7 +1528,7 @@ class PermissionService(private val service: AccessCheckingService) : } val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues) - setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags) + setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags) } override fun getAllowlistedRestrictedPermissions( @@ -1648,11 +1603,11 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - persistentDeviceId: String, + deviceId: String, ): Int { return if ( !Flags.deviceAwarePermissionApisEnabled() || - persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT ) { with(policy) { getPermissionFlags(appId, userId, permissionName) } } else { @@ -1664,9 +1619,7 @@ class PermissionService(private val service: AccessCheckingService) : ) return with(policy) { getPermissionFlags(appId, userId, permissionName) } } - with(devicePolicy) { - getPermissionFlags(appId, persistentDeviceId, userId, permissionName) - } + with(devicePolicy) { getPermissionFlags(appId, deviceId, userId, permissionName) } } } @@ -1674,12 +1627,12 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - persistentDeviceId: String, + deviceId: String, flags: Int ): Boolean { return if ( !Flags.deviceAwarePermissionApisEnabled() || - persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT ) { with(policy) { setPermissionFlags(appId, userId, permissionName, flags) } } else { @@ -1693,23 +1646,11 @@ class PermissionService(private val service: AccessCheckingService) : } with(devicePolicy) { - setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags) + setPermissionFlags(appId, deviceId, userId, permissionName, flags) } } } - private fun getPersistentDeviceId(deviceId: Int): String? { - if (deviceId == Context.DEVICE_ID_DEFAULT) { - return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT - } - - if (virtualDeviceManagerInternal == null) { - virtualDeviceManagerInternal = - LocalServices.getService(VirtualDeviceManagerInternal::class.java) - } - return virtualDeviceManagerInternal?.getPersistentIdForDevice(deviceId) - } - /** * This method does not enforce checks on the caller, should only be called after required * checks. @@ -2270,9 +2211,9 @@ class PermissionService(private val service: AccessCheckingService) : userState.appIdDevicePermissionFlags[appId]?.forEachIndexed { _, - persistentDeviceId, + deviceId, devicePermissionFlags -> - println("Permissions (Device $persistentDeviceId):") + println("Permissions (Device $deviceId):") withIndent { devicePermissionFlags.forEachIndexed { _, permissionName, flags -> val isGranted = PermissionFlags.isPermissionGranted(flags) @@ -2415,9 +2356,8 @@ class PermissionService(private val service: AccessCheckingService) : with(devicePolicy) { trimDevicePermissionStates(persistentDeviceIds) } } } - virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { persistentDeviceId - -> - service.mutateState { with(devicePolicy) { onDeviceIdRemoved(persistentDeviceId) } } + virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { deviceId -> + service.mutateState { with(devicePolicy) { onDeviceIdRemoved(deviceId) } } } permissionControllerManager = @@ -2764,7 +2704,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun onDevicePermissionFlagsChanged( appId: Int, userId: Int, - persistentDeviceId: String, + deviceId: String, permissionName: String, oldFlags: Int, newFlags: Int @@ -2787,8 +2727,7 @@ class PermissionService(private val service: AccessCheckingService) : permissionName in NOTIFICATIONS_PERMISSIONS && runtimePermissionRevokedUids.get(uid, true) } - runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += - persistentDeviceId + runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId } if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) { @@ -2803,8 +2742,8 @@ class PermissionService(private val service: AccessCheckingService) : } runtimePermissionChangedUidDevices.forEachIndexed { _, uid, persistentDeviceIds -> - persistentDeviceIds.forEach { persistentDeviceId -> - onPermissionsChangeListeners.onPermissionsChanged(uid, persistentDeviceId) + persistentDeviceIds.forEach { deviceId -> + onPermissionsChangeListeners.onPermissionsChanged(uid, deviceId) } } runtimePermissionChangedUidDevices.clear() @@ -2844,8 +2783,11 @@ class PermissionService(private val service: AccessCheckingService) : private fun isAppBackupAndRestoreRunning(uid: Int): Boolean { if ( - checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) != - PackageManager.PERMISSION_GRANTED + checkUidPermission( + uid, + Manifest.permission.BACKUP, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + ) != PackageManager.PERMISSION_GRANTED ) { return false } @@ -2879,16 +2821,16 @@ class PermissionService(private val service: AccessCheckingService) : when (msg.what) { MSG_ON_PERMISSIONS_CHANGED -> { val uid = msg.arg1 - val persistentDeviceId = msg.obj as String - handleOnPermissionsChanged(uid, persistentDeviceId) + val deviceId = msg.obj as String + handleOnPermissionsChanged(uid, deviceId) } } } - private fun handleOnPermissionsChanged(uid: Int, persistentDeviceId: String) { + private fun handleOnPermissionsChanged(uid: Int, deviceId: String) { listeners.broadcast { listener -> try { - listener.onPermissionsChanged(uid, persistentDeviceId) + listener.onPermissionsChanged(uid, deviceId) } catch (e: RemoteException) { Slog.e(LOG_TAG, "Error when calling OnPermissionsChangeListener", e) } @@ -2903,9 +2845,9 @@ class PermissionService(private val service: AccessCheckingService) : listeners.unregister(listener) } - fun onPermissionsChanged(uid: Int, persistentDeviceId: String) { + fun onPermissionsChanged(uid: Int, deviceId: String) { if (listeners.registeredCallbackCount > 0) { - obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId).sendToTarget() + obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget() } } 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 cfe701f42065..d4b57f191ecd 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 @@ -273,7 +273,8 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag AndroidPackage::hasRequestForegroundServiceExemption, AndroidPackage::hasRequestRawExternalStorageAccess, AndroidPackage::isUpdatableSystem, - AndroidPackage::getEmergencyInstaller + AndroidPackage::getEmergencyInstaller, + AndroidPackage::isAllowCrossUidActivitySwitchFromBelow, ) override fun extraParams() = listOf( diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 57b86324e171..807774f90655 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -725,8 +725,8 @@ public final class DisplayPowerControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { + when(mDisplayManagerFlagsMock.isFastHdrTransitionsEnabled()).thenReturn(true); Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); @@ -757,12 +757,12 @@ public final class DisplayPowerControllerTest { advanceTime(1); // Run updatePowerState verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { + when(mDisplayManagerFlagsMock.isFastHdrTransitionsEnabled()).thenReturn(true); Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); @@ -797,7 +797,7 @@ public final class DisplayPowerControllerTest { advanceTime(1); // Run updatePowerState verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java index 9473e572259f..c298d516c89e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java @@ -18,7 +18,7 @@ package com.android.server; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; +import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index d876dae29798..47928bcffb9a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -72,6 +72,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.answer; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; @@ -157,9 +159,11 @@ public class MockingOomAdjusterTests { private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE; private static final int MOCKAPP_ISOLATED_UID = Process.FIRST_ISOLATED_UID + 321; private static final String MOCKAPP_ISOLATED_PROCESSNAME = "isolated test #1"; + private static final int MOCKAPP_SDK_SANDBOX_UID = Process.FIRST_SDK_SANDBOX_UID + 654; + private static final String MOCKAPP_SDK_SANDBOX_PROCESSNAME = "sandbox test #1"; private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ - + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; + + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; private static Context sContext; private static PackageManagerInternal sPackageManagerInternal; private static ActivityManagerService sService; @@ -271,7 +275,6 @@ public class MockingOomAdjusterTests { /** * Replace the process LRU with the given processes. - * @param apps */ @SuppressWarnings("GuardedBy") private void setProcessesToLru(ProcessRecord... apps) { @@ -660,7 +663,7 @@ public class MockingOomAdjusterTests { app.mState.setLastTopTime(nowUptime); // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, - null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); @@ -682,7 +685,7 @@ public class MockingOomAdjusterTests { app.mState.setLastTopTime(nowUptime); // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, - null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class)); + null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; app.mServices.updateHasTopStartedAlmostPerceptibleServices(); @@ -704,7 +707,7 @@ public class MockingOomAdjusterTests { app.mState.setLastTopTime(nowUptime); // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, - null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; s.getConnections().clear(); @@ -729,7 +732,7 @@ public class MockingOomAdjusterTests { system.mState.setHasTopUi(true); // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, - null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(system, app); @@ -901,7 +904,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY, + ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY, mock(IBinder.class)); s.startRequested = true; sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -921,7 +924,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setTreatLikeActivity(true); - bindService(app, client, null, Context.BIND_WAIVE_PRIORITY + bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -937,7 +940,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); IBinder binder = mock(IBinder.class); - ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY + ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_IMPORTANT, binder); ConnectionRecord cr = s.getConnections().get(binder).get(0); setFieldValue(ConnectionRecord.class, cr, "activity", @@ -955,7 +958,7 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_Service_Self() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); - bindService(app, app, null, 0, mock(IBinder.class)); + bindService(app, app, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -970,7 +973,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setTreatLikeActivity(true); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -988,7 +991,8 @@ public class MockingOomAdjusterTests { doReturn(true).when(wpc).hasActivities(); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT, + mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); doReturn(client).when(sService).getTopApp(); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -1005,7 +1009,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client.mState.setHasTopUi(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -1023,7 +1027,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class)); client.mServices.startExecutingService(mock(ServiceRecord.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1039,7 +1043,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); doReturn(client).when(sService).getTopApp(); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -1056,7 +1060,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1074,7 +1078,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1090,7 +1094,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1109,7 +1113,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); @@ -1172,8 +1176,8 @@ public class MockingOomAdjusterTests { ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app1, pers, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); - bindService(app2, app1, null, 0, mock(IBinder.class)); + bindService(app1, pers, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); + bindService(app2, app1, null, null, 0, mock(IBinder.class)); updateOomAdj(pers, app1, app2); @@ -1192,7 +1196,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); @@ -1218,7 +1222,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1233,7 +1237,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1248,7 +1252,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setHasOverlayUi(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1264,7 +1268,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, + bindService(app, client, null, null, Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); @@ -1283,7 +1287,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); WindowProcessController wpc = client.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - bindService(app, client, null, + bindService(app, client, null, null, Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); @@ -1301,7 +1305,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, + bindService(app, client, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); @@ -1320,7 +1324,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); WindowProcessController wpc = client.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - bindService(app, client, null, + bindService(app, client, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); @@ -1341,7 +1345,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1356,7 +1360,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND, + mock(IBinder.class)); client.mState.setHasOverlayUi(true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1496,10 +1501,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); doReturn(client2).when(sService).getTopApp(); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -1517,10 +1522,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app, client2, null, 0, mock(IBinder.class)); + bindService(app, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); @@ -1537,10 +1542,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); @@ -1557,12 +1562,12 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific // processes. @@ -1599,11 +1604,11 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); - bindService(client, app, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); + bindService(client, app, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client2, client, null, 0, mock(IBinder.class)); + bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); @@ -1626,11 +1631,11 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); - bindService(client2, client, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); + bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); @@ -1653,18 +1658,18 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); - bindService(client2, client, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); + bindService(client2, client, null, null, 0, mock(IBinder.class)); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); - bindService(client3, client, null, 0, mock(IBinder.class)); + bindService(client3, client, null, null, 0, mock(IBinder.class)); ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); - bindService(client3, client4, null, 0, mock(IBinder.class)); - bindService(client4, client3, null, 0, mock(IBinder.class)); + bindService(client3, client4, null, null, 0, mock(IBinder.class)); + bindService(client4, client3, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); @@ -1693,16 +1698,16 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); - bindService(app, client3, null, 0, mock(IBinder.class)); + bindService(app, client3, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); @@ -1718,17 +1723,17 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = client2.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); - bindService(app, client3, null, 0, mock(IBinder.class)); + bindService(app, client3, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); @@ -1743,11 +1748,11 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = client2.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, @@ -1755,7 +1760,7 @@ public class MockingOomAdjusterTests { ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mState.setForcingToImportant(new Object()); - bindService(app, client4, null, 0, mock(IBinder.class)); + bindService(app, client4, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); @@ -1770,21 +1775,21 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, 0, mock(IBinder.class)); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client, client2, null, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = client2.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); - bindService(app, client3, null, 0, mock(IBinder.class)); + bindService(app, client3, null, null, 0, mock(IBinder.class)); ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app, client4, null, 0, mock(IBinder.class)); + bindService(app, client4, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); @@ -1802,15 +1807,15 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); WindowProcessController wpc = client.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app, client2, null, 0, mock(IBinder.class)); + bindService(app, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); - bindService(app, client3, null, 0, mock(IBinder.class)); + bindService(app, client3, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, client3, app); @@ -1826,7 +1831,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); @@ -1846,12 +1851,12 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, 0, mock(IBinder.class)); + bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(client2, app, null, 0, mock(IBinder.class)); + bindService(client2, app, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); @@ -1912,9 +1917,9 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); - bindService(app1, client1, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, + bindService(app1, client1, null, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, mock(IBinder.class)); - bindService(app2, client2, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, + bindService(app2, client2, null, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, mock(IBinder.class)); client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); @@ -1929,8 +1934,10 @@ public class MockingOomAdjusterTests { assertBfsl(app1); assertBfsl(app2); - bindService(app1, client1, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class)); - bindService(app2, client2, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class)); + bindService(app1, client1, null, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, + mock(IBinder.class)); + bindService(app2, client2, null, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, + mock(IBinder.class)); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1946,8 +1953,8 @@ public class MockingOomAdjusterTests { SCHED_GROUP_DEFAULT); assertBfsl(app2); - bindService(client2, app1, null, 0, mock(IBinder.class)); - bindService(app1, client2, null, 0, mock(IBinder.class)); + bindService(client2, app1, null, null, 0, mock(IBinder.class)); + bindService(app1, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false); updateOomAdj(app1, client1, client2); assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ, @@ -1968,9 +1975,9 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client2.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - final ServiceRecord s1 = bindService(app1, client1, null, + final ServiceRecord s1 = bindService(app1, client1, null, null, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class)); - final ServiceRecord s2 = bindService(app2, client2, null, + final ServiceRecord s2 = bindService(app2, client2, null, null, Context.BIND_IMPORTANT, mock(IBinder.class)); updateOomAdj(client1, client2, app1, app2); @@ -1980,7 +1987,7 @@ public class MockingOomAdjusterTests { assertProcStates(app2, PROCESS_STATE_PERSISTENT, PERSISTENT_SERVICE_ADJ, SCHED_GROUP_DEFAULT); - bindService(app2, client1, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, + bindService(app2, client1, null, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class)); updateOomAdj(app2); assertProcStates(app2, PROCESS_STATE_PERSISTENT, PERSISTENT_SERVICE_ADJ, @@ -1995,9 +2002,9 @@ public class MockingOomAdjusterTests { client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); client2.mState.setHasOverlayUi(true); - bindService(app1, client1, s1, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, + bindService(app1, client1, null, s1, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class)); - bindService(app2, client2, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, + bindService(app2, client2, null, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class)); updateOomAdj(client1, client2, app1, app2); @@ -2030,7 +2037,7 @@ public class MockingOomAdjusterTests { app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - bindService(app1, client1, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); + bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); updateOomAdj(client1, app1); @@ -2051,7 +2058,8 @@ public class MockingOomAdjusterTests { app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - bindService(app1, client1, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE, + mock(IBinder.class)); updateOomAdj(client1, app1); @@ -2121,19 +2129,19 @@ public class MockingOomAdjusterTests { final ComponentName cn1 = ComponentName.unflattenFromString( MOCKAPP_PACKAGENAME + "/.TestService"); - final ServiceRecord s1 = bindService(app1, client1, null, 0, mock(IBinder.class)); + final ServiceRecord s1 = bindService(app1, client1, null, null, 0, mock(IBinder.class)); setFieldValue(ServiceRecord.class, s1, "name", cn1); s1.startRequested = true; final ComponentName cn2 = ComponentName.unflattenFromString( MOCKAPP2_PACKAGENAME + "/.TestService"); - final ServiceRecord s2 = bindService(app2, client2, null, 0, mock(IBinder.class)); + final ServiceRecord s2 = bindService(app2, client2, null, null, 0, mock(IBinder.class)); setFieldValue(ServiceRecord.class, s2, "name", cn2); s2.startRequested = true; final ComponentName cn3 = ComponentName.unflattenFromString( MOCKAPP5_PACKAGENAME + "/.TestService"); - final ServiceRecord s3 = bindService(app3, client1, null, 0, mock(IBinder.class)); + final ServiceRecord s3 = bindService(app3, client1, null, null, 0, mock(IBinder.class)); setFieldValue(ServiceRecord.class, s3, "name", cn3); s3.startRequested = true; @@ -2177,7 +2185,7 @@ public class MockingOomAdjusterTests { clientUidRecord.setIdle(true); doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), - anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); + anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); doNothing().when(sService.mServices) .scheduleServiceTimeoutLocked(any(ProcessRecord.class)); updateOomAdj(client1, client2, app1, app2, app3); @@ -2188,7 +2196,7 @@ public class MockingOomAdjusterTests { } finally { doCallRealMethod().when(sService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), - anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); + anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); sService.mServices.mServiceMap.clear(); sService.mOomAdjuster.mActiveUids.clear(); } @@ -2223,7 +2231,7 @@ public class MockingOomAdjusterTests { ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app, app2, null, 0, mock(IBinder.class)); + bindService(app, app2, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); @@ -2242,12 +2250,12 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, app2, null, 0, mock(IBinder.class)); + bindService(app, app2, null, null, 0, mock(IBinder.class)); ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app2, app3, null, 0, mock(IBinder.class)); + bindService(app2, app3, null, null, 0, mock(IBinder.class)); app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app3, app, null, 0, mock(IBinder.class)); + bindService(app3, app, null, null, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3); @@ -2278,21 +2286,21 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class)); ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app2, app3, null, 0, mock(IBinder.class)); - bindService(app3, app, null, 0, mock(IBinder.class)); + bindService(app2, app3, null, null, 0, mock(IBinder.class)); + bindService(app3, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = app3.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); app4.mState.setHasOverlayUi(true); - bindService(app, app4, s, 0, mock(IBinder.class)); + bindService(app, app4, null, s, 0, mock(IBinder.class)); ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app, app5, s, 0, mock(IBinder.class)); + bindService(app, app5, null, s, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); @@ -2320,21 +2328,21 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class)); ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app2, app3, null, 0, mock(IBinder.class)); - bindService(app3, app, null, 0, mock(IBinder.class)); + bindService(app2, app3, null, null, 0, mock(IBinder.class)); + bindService(app3, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = app3.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); app4.mState.setHasOverlayUi(true); - bindService(app, app4, s, 0, mock(IBinder.class)); + bindService(app, app4, null, s, 0, mock(IBinder.class)); ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app, app5, s, 0, mock(IBinder.class)); + bindService(app, app5, null, s, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app5, app4, app3, app2, app); @@ -2362,21 +2370,21 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class)); ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(app2, app3, null, 0, mock(IBinder.class)); - bindService(app3, app, null, 0, mock(IBinder.class)); + bindService(app2, app3, null, null, 0, mock(IBinder.class)); + bindService(app3, app, null, null, 0, mock(IBinder.class)); WindowProcessController wpc = app3.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); app4.mState.setHasOverlayUi(true); - bindService(app, app4, s, 0, mock(IBinder.class)); + bindService(app, app4, null, s, 0, mock(IBinder.class)); ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - bindService(app, app5, s, 0, mock(IBinder.class)); + bindService(app, app5, null, s, 0, mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app3, app4, app2, app, app5); @@ -2404,15 +2412,19 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + bindService(app, client, null, null, Context.BIND_INCLUDE_CAPABILITIES, + mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); - bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + bindService(client, client2, null, null, Context.BIND_INCLUDE_CAPABILITIES, + mock(IBinder.class)); + bindService(client2, app, null, null, Context.BIND_INCLUDE_CAPABILITIES, + mock(IBinder.class)); ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); + bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES, + mock(IBinder.class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); @@ -2472,10 +2484,10 @@ public class MockingOomAdjusterTests { ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); long now = SystemClock.uptimeMillis(); - ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class)); s.startRequested = true; s.lastActivity = now; - s = bindService(app2, app, null, 0, mock(IBinder.class)); + s = bindService(app2, app, null, null, 0, mock(IBinder.class)); s.startRequested = true; s.lastActivity = now; ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, @@ -2507,11 +2519,11 @@ public class MockingOomAdjusterTests { final int userOwner = 0; final int userOther = 1; final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ - ? CACHED_APP_MIN_ADJ + 10 - : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; + ? CACHED_APP_MIN_ADJ + 10 + : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ - ? CACHED_APP_MIN_ADJ + 10 - : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; + ? CACHED_APP_MIN_ADJ + 10 + : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; doReturn(userOwner).when(sService.mUserController).getCurrentUserId(); final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); @@ -2626,7 +2638,7 @@ public class MockingOomAdjusterTests { // Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and // verify that its OOM adjustment level is unaffected. - bindService(app, app, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); + bindService(app, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); app.mServices.updateHasAboveClientLocked(); assertFalse(app.mServices.hasAboveClient()); @@ -2644,12 +2656,12 @@ public class MockingOomAdjusterTests { final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); long now = SystemClock.uptimeMillis(); - ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class)); s.startRequested = true; s.lastActivity = now; - s = bindService(app2, app3, null, 0, mock(IBinder.class)); + s = bindService(app2, app3, null, null, 0, mock(IBinder.class)); s.lastActivity = now; - s = bindService(app3, app2, null, 0, mock(IBinder.class)); + s = bindService(app3, app2, null, null, 0, mock(IBinder.class)); s.lastActivity = now; sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -2678,7 +2690,7 @@ public class MockingOomAdjusterTests { // Start binding to a service that isn't running yet. ServiceRecord sr = makeServiceRecord(app); sr.app = null; - bindService(null, app, sr, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); + bindService(null, app, null, sr, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); // Since sr.app is null, this service cannot be in the same process as the // client so we expect the BIND_ABOVE_CLIENT adjustment to take effect. @@ -2772,91 +2784,37 @@ public class MockingOomAdjusterTests { ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true); } + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_SdkSandbox_attributedClient() { + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + ProcessRecord attributedClient = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, + MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, true)); + ProcessRecord sandboxService = spy(new ProcessRecordBuilder(MOCKAPP_PID, + MOCKAPP_SDK_SANDBOX_UID, MOCKAPP_SDK_SANDBOX_PROCESSNAME, MOCKAPP_PACKAGENAME) + .setSdkSandboxClientAppPackage(MOCKAPP3_PACKAGENAME) + .build()); + + setProcessesToLru(sandboxService, client, attributedClient); + + client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + attributedClient.mServices.setHasForegroundServices(true, 0, true); + bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class)); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + updateOomAdj(); + assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(attributedClient, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(sandboxService, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { - long now = SystemClock.uptimeMillis(); - return makeProcessRecord(sService, pid, uid, processName, - packageName, 12345, Build.VERSION_CODES.CUR_DEVELOPMENT, - now, now, now, 12345, UNKNOWN_ADJ, UNKNOWN_ADJ, - UNKNOWN_ADJ, CACHED_APP_MAX_ADJ, - SCHED_GROUP_DEFAULT, SCHED_GROUP_DEFAULT, - PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT, - PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT, - 0, 0, false, false, false, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE, - false, false, false, hasShownUi, false, false, false, false, false, false, null, - 0, Long.MIN_VALUE, Long.MIN_VALUE, true, 0, null, false); - } - - private ProcessRecord makeProcessRecord(ActivityManagerService service, int pid, int uid, - String processName, String packageName, long versionCode, int targetSdkVersion, - long lastActivityTime, long lastPssTime, long nextPssTime, long lastPss, int maxAdj, - int setRawAdj, int curAdj, int setAdj, int curSchedGroup, int setSchedGroup, - int curProcState, int repProcState, int curRawProcState, int setProcState, - int connectionGroup, int connectionImportance, boolean serviceb, - boolean hasClientActivities, boolean hasForegroundServices, int fgServiceTypes, - boolean hasForegroundActivities, boolean repForegroundActivities, boolean systemNoUi, - boolean hasShownUi, boolean hasTopUi, boolean hasOverlayUi, - boolean runningRemoteAnimation, boolean hasAboveClient, boolean treatLikeActivity, - boolean killedByAm, Object forcingToImportant, int numOfCurReceivers, - long lastProviderTime, long lastTopTime, boolean cached, int numOfExecutingServices, - String isolatedEntryPoint, boolean execServicesFg) { - ApplicationInfo ai = spy(new ApplicationInfo()); - ai.uid = uid; - ai.packageName = packageName; - ai.longVersionCode = versionCode; - ai.targetSdkVersion = targetSdkVersion; - ProcessRecord app = new ProcessRecord(service, ai, processName, uid); - final ProcessStateRecord state = app.mState; - final ProcessServiceRecord services = app.mServices; - final ProcessReceiverRecord receivers = app.mReceivers; - final ProcessProfileRecord profile = app.mProfile; - final ProcessProviderRecord providers = app.mProviders; - app.makeActive(mock(IApplicationThread.class), sService.mProcessStats); - app.setLastActivityTime(lastActivityTime); - app.setKilledByAm(killedByAm); - app.setIsolatedEntryPoint(isolatedEntryPoint); - setFieldValue(ProcessRecord.class, app, "mWindowProcessController", - mock(WindowProcessController.class)); - profile.setLastPssTime(lastPssTime); - profile.setNextPssTime(nextPssTime); - profile.setLastPss(lastPss); - state.setMaxAdj(maxAdj); - state.setSetRawAdj(setRawAdj); - state.setCurAdj(curAdj); - state.setSetAdj(setAdj); - state.setCurrentSchedulingGroup(curSchedGroup); - state.setSetSchedGroup(setSchedGroup); - state.setCurProcState(curProcState); - state.setReportedProcState(repProcState); - state.setCurRawProcState(curRawProcState); - state.setSetProcState(setProcState); - state.setServiceB(serviceb); - state.setRepForegroundActivities(repForegroundActivities); - state.setHasForegroundActivities(hasForegroundActivities); - state.setSystemNoUi(systemNoUi); - state.setHasShownUi(hasShownUi); - state.setHasTopUi(hasTopUi); - state.setRunningRemoteAnimation(runningRemoteAnimation); - state.setHasOverlayUi(hasOverlayUi); - state.setCached(cached); - state.setLastTopTime(lastTopTime); - state.setForcingToImportant(forcingToImportant); - services.setConnectionGroup(connectionGroup); - services.setConnectionImportance(connectionImportance); - services.setHasClientActivities(hasClientActivities); - services.setHasForegroundServices(hasForegroundServices, fgServiceTypes, - /* hasNoneType=*/false); - services.setHasAboveClient(hasAboveClient); - services.setTreatLikeActivity(treatLikeActivity); - services.setExecServicesFg(execServicesFg); - for (int i = 0; i < numOfExecutingServices; i++) { - services.startExecutingService(mock(ServiceRecord.class)); - } - for (int i = 0; i < numOfCurReceivers; i++) { - receivers.addCurReceiver(mock(BroadcastRecord.class)); - } - providers.setLastProviderTime(lastProviderTime); - return app; + return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi( + hasShownUi).build(); } private ServiceRecord makeServiceRecord(ProcessRecord app) { @@ -2870,6 +2828,7 @@ public class MockingOomAdjusterTests { record.appInfo = app.info; setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>()); setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>()); + setFieldValue(ServiceRecord.class, record, "isSdkSandbox", app.isSdkSandbox); return record; } @@ -2892,11 +2851,11 @@ public class MockingOomAdjusterTests { } private ServiceRecord bindService(ProcessRecord service, ProcessRecord client, - ServiceRecord record, long bindFlags, IBinder binder) { + ProcessRecord attributedClient, ServiceRecord record, long bindFlags, IBinder binder) { if (record == null) { record = makeServiceRecord(service); } - AppBindRecord binding = new AppBindRecord(record, null, client, null); + AppBindRecord binding = new AppBindRecord(record, null, client, attributedClient); ConnectionRecord cr = spy(new ConnectionRecord(binding, mock(ActivityServiceConnectionsHolder.class), mock(IServiceConnection.class), bindFlags, @@ -2961,4 +2920,140 @@ public class MockingOomAdjusterTests { assertBfsl(app); } } + + private static class ProcessRecordBuilder { + @SuppressWarnings("UnusedVariable") + int mPid; + int mUid; + String mProcessName; + String mPackageName; + long mVersionCode = 12345; + int mTargetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + long mLastActivityTime; + long mLastPssTime; + long mNextPssTime; + long mLastPss = 12345; + int mMaxAdj = UNKNOWN_ADJ; + int mSetRawAdj = UNKNOWN_ADJ; + int mCurAdj = UNKNOWN_ADJ; + int mSetAdj = CACHED_APP_MAX_ADJ; + int mCurSchedGroup = SCHED_GROUP_DEFAULT; + int mSetSchedGroup = SCHED_GROUP_DEFAULT; + int mCurProcState = PROCESS_STATE_NONEXISTENT; + int mRepProcState = PROCESS_STATE_NONEXISTENT; + int mCurRawProcState = PROCESS_STATE_NONEXISTENT; + int mSetProcState = PROCESS_STATE_NONEXISTENT; + int mConnectionGroup = 0; + int mConnectionImportance = 0; + boolean mServiceb = false; + boolean mHasClientActivities = false; + boolean mHasForegroundServices = false; + int mFgServiceTypes = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; + boolean mHasForegroundActivities = false; + boolean mRepForegroundActivities = false; + boolean mSystemNoUi = false; + boolean mHasShownUi = false; + boolean mHasTopUi = false; + boolean mHasOverlayUi = false; + boolean mRunningRemoteAnimation = false; + boolean mHasAboveClient = false; + boolean mTreatLikeActivity = false; + boolean mKilledByAm = false; + Object mForcingToImportant; + int mNumOfCurReceivers = 0; + long mLastProviderTime = Long.MIN_VALUE; + long mLastTopTime = Long.MIN_VALUE; + boolean mCached = true; + int mNumOfExecutingServices = 0; + String mIsolatedEntryPoint = null; + boolean mExecServicesFg = false; + String mSdkSandboxClientAppPackage = null; + + ProcessRecordBuilder(int pid, int uid, String processName, String packageName) { + mPid = pid; + mUid = uid; + mProcessName = processName; + mPackageName = packageName; + + long now = SystemClock.uptimeMillis(); + mLastActivityTime = now; + mLastPssTime = now; + mNextPssTime = now; + } + + ProcessRecordBuilder setHasShownUi(boolean hasShownUi) { + mHasShownUi = hasShownUi; + return this; + } + + ProcessRecordBuilder setSdkSandboxClientAppPackage(String sdkSandboxClientAppPackage) { + mSdkSandboxClientAppPackage = sdkSandboxClientAppPackage; + return this; + } + + @SuppressWarnings("GuardedBy") + public ProcessRecord build() { + ApplicationInfo ai = spy(new ApplicationInfo()); + ai.uid = mUid; + ai.packageName = mPackageName; + ai.longVersionCode = mVersionCode; + ai.targetSdkVersion = mTargetSdkVersion; + doCallRealMethod().when(sService).getPackageManagerInternal(); + doReturn(null).when(sPackageManagerInternal).getApplicationInfo( + eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt()); + ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid, + mSdkSandboxClientAppPackage, -1, null); + final ProcessStateRecord state = app.mState; + final ProcessServiceRecord services = app.mServices; + final ProcessReceiverRecord receivers = app.mReceivers; + final ProcessProfileRecord profile = app.mProfile; + final ProcessProviderRecord providers = app.mProviders; + app.makeActive(mock(IApplicationThread.class), sService.mProcessStats); + app.setLastActivityTime(mLastActivityTime); + app.setKilledByAm(mKilledByAm); + app.setIsolatedEntryPoint(mIsolatedEntryPoint); + setFieldValue(ProcessRecord.class, app, "mWindowProcessController", + mock(WindowProcessController.class)); + profile.setLastPssTime(mLastPssTime); + profile.setNextPssTime(mNextPssTime); + profile.setLastPss(mLastPss); + state.setMaxAdj(mMaxAdj); + state.setSetRawAdj(mSetRawAdj); + state.setCurAdj(mCurAdj); + state.setSetAdj(mSetAdj); + state.setCurrentSchedulingGroup(mCurSchedGroup); + state.setSetSchedGroup(mSetSchedGroup); + state.setCurProcState(mCurProcState); + state.setReportedProcState(mRepProcState); + state.setCurRawProcState(mCurRawProcState); + state.setSetProcState(mSetProcState); + state.setServiceB(mServiceb); + state.setRepForegroundActivities(mRepForegroundActivities); + state.setHasForegroundActivities(mHasForegroundActivities); + state.setSystemNoUi(mSystemNoUi); + state.setHasShownUi(mHasShownUi); + state.setHasTopUi(mHasTopUi); + state.setRunningRemoteAnimation(mRunningRemoteAnimation); + state.setHasOverlayUi(mHasOverlayUi); + state.setCached(mCached); + state.setLastTopTime(mLastTopTime); + state.setForcingToImportant(mForcingToImportant); + services.setConnectionGroup(mConnectionGroup); + services.setConnectionImportance(mConnectionImportance); + services.setHasClientActivities(mHasClientActivities); + services.setHasForegroundServices(mHasForegroundServices, mFgServiceTypes, + /* hasNoneType=*/false); + services.setHasAboveClient(mHasAboveClient); + services.setTreatLikeActivity(mTreatLikeActivity); + services.setExecServicesFg(mExecServicesFg); + for (int i = 0; i < mNumOfExecutingServices; i++) { + services.startExecutingService(mock(ServiceRecord.class)); + } + for (int i = 0; i < mNumOfCurReceivers; i++) { + receivers.addCurReceiver(mock(BroadcastRecord.class)); + } + providers.setLastProviderTime(mLastProviderTime); + return app; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java new file mode 100644 index 000000000000..08a65292cf20 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.adaptiveauth; + +import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH; +import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS; +import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; + +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; +import static com.android.server.adaptiveauth.AdaptiveAuthService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.KeyguardManager; +import android.content.Context; +import android.hardware.biometrics.AuthenticationStateListener; +import android.hardware.biometrics.BiometricManager; +import android.os.RemoteException; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockSettingsInternal; +import com.android.internal.widget.LockSettingsStateListener; +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; +import com.android.server.wm.WindowManagerInternal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * atest FrameworksServicesTests:AdaptiveAuthServiceTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AdaptiveAuthServiceTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private static final int PRIMARY_USER_ID = 0; + private static final int MANAGED_PROFILE_USER_ID = 12; + private static final int DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS = 0; + private static final int REASON_UNKNOWN = 0; // BiometricRequestConstants.RequestReason + + private Context mContext; + private AdaptiveAuthService mAdaptiveAuthService; + + @Mock + LockPatternUtils mLockPatternUtils; + @Mock + private LockSettingsInternal mLockSettings; + @Mock + private BiometricManager mBiometricManager; + @Mock + private KeyguardManager mKeyguardManager; + @Mock + private WindowManagerInternal mWindowManager; + @Mock + private UserManagerInternal mUserManager; + + @Captor + ArgumentCaptor<LockSettingsStateListener> mLockSettingsStateListenerCaptor; + @Captor + ArgumentCaptor<AuthenticationStateListener> mAuthenticationStateListenerCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mSetFlagsRule.enableFlags(FLAG_ENABLE_ADAPTIVE_AUTH); + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS); + + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager); + when(mContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager); + + LocalServices.removeServiceForTest(LockSettingsInternal.class); + LocalServices.addService(LockSettingsInternal.class, mLockSettings); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.addService(WindowManagerInternal.class, mWindowManager); + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, mUserManager); + + mAdaptiveAuthService = new AdaptiveAuthService(mContext, mLockPatternUtils); + mAdaptiveAuthService.init(); + + verify(mLockSettings).registerLockSettingsStateListener( + mLockSettingsStateListenerCaptor.capture()); + verify(mBiometricManager).registerAuthenticationStateListener( + mAuthenticationStateListenerCaptor.capture()); + + // Set PRIMARY_USER_ID as the parent of MANAGED_PROFILE_USER_ID + when(mUserManager.getProfileParentId(eq(MANAGED_PROFILE_USER_ID))) + .thenReturn(PRIMARY_USER_ID); + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(LockSettingsInternal.class); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); + } + + @Test + public void testReportAuthAttempt_primaryAuthSucceeded() + throws RemoteException { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationSucceeded(PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthFailed_once() + throws RemoteException { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(1 /* expectedCntFailedAttempts */, PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthFailed_multiple_deviceCurrentlyLocked() + throws RemoteException { + // Device is currently locked and Keyguard is showing + when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(true); + when(mKeyguardManager.isKeyguardLocked()).thenReturn(true); + + for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID); + } + waitForAuthCompletion(); + + verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthFailed_multiple_deviceCurrentlyNotLocked() + throws RemoteException { + // Device is currently not locked and Keyguard is not showing + when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false); + when(mKeyguardManager.isKeyguardLocked()).thenReturn(false); + + for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID); + } + waitForAuthCompletion(); + + verifyLockDevice(PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_biometricAuthSucceeded() + throws RemoteException { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationSucceeded(REASON_UNKNOWN, PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_biometricAuthFailed_once() + throws RemoteException { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(1 /* expectedCntFailedAttempts */, PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyLocked() + throws RemoteException { + // Device is currently locked and Keyguard is showing + when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(true); + when(mKeyguardManager.isKeyguardLocked()).thenReturn(true); + + for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID); + } + waitForAuthCompletion(); + + verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked() + throws RemoteException { + // Device is currently not locked and Keyguard is not showing + when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false); + when(mKeyguardManager.isKeyguardLocked()).thenReturn(false); + + for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID); + } + waitForAuthCompletion(); + + verifyLockDevice(PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_biometricAuthFailedThenPrimaryAuthSucceeded() + throws RemoteException { + // Three failed biometric auth attempts + for (int i = 0; i < 3; i++) { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID); + } + // One successful primary auth attempt + mLockSettingsStateListenerCaptor.getValue().onAuthenticationSucceeded(PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthFailedThenBiometricAuthSucceeded() + throws RemoteException { + // Three failed primary auth attempts + for (int i = 0; i < 3; i++) { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID); + } + // One successful biometric auth attempt + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationSucceeded(REASON_UNKNOWN, PRIMARY_USER_ID); + waitForAuthCompletion(); + + verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */, + PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser() + throws RemoteException { + // Three failed primary auth attempts + for (int i = 0; i < 3; i++) { + mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID); + } + // Two failed biometric auth attempts + for (int i = 0; i < 2; i++) { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID); + } + waitForAuthCompletion(); + + verifyLockDevice(PRIMARY_USER_ID); + } + + @Test + public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_profileOfPrimaryUser() + throws RemoteException { + // Three failed primary auth attempts + for (int i = 0; i < 3; i++) { + mLockSettingsStateListenerCaptor.getValue() + .onAuthenticationFailed(MANAGED_PROFILE_USER_ID); + } + // Two failed biometric auth attempts + for (int i = 0; i < 2; i++) { + mAuthenticationStateListenerCaptor.getValue() + .onAuthenticationFailed(REASON_UNKNOWN, MANAGED_PROFILE_USER_ID); + } + waitForAuthCompletion(); + + verifyLockDevice(MANAGED_PROFILE_USER_ID); + } + + private void verifyNotLockDevice(int expectedCntFailedAttempts, int userId) { + assertEquals(expectedCntFailedAttempts, + mAdaptiveAuthService.mFailedAttemptsForUser.get(userId)); + verify(mWindowManager, never()).lockNow(); + } + + private void verifyLockDevice(int userId) { + assertEquals(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, + mAdaptiveAuthService.mFailedAttemptsForUser.get(userId)); + verify(mLockPatternUtils).requireStrongAuth( + eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(userId)); + // If userId is MANAGED_PROFILE_USER_ID, the StrongAuthFlag of its parent (PRIMARY_USER_ID) + // should also be verified + if (userId == MANAGED_PROFILE_USER_ID) { + verify(mLockPatternUtils).requireStrongAuth( + eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(PRIMARY_USER_ID)); + } + verify(mWindowManager).lockNow(); + } + + /** + * Wait for all auth events to complete before verification + */ + private static void waitForAuthCompletion() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS new file mode 100644 index 000000000000..0218a7835586 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/adaptiveauth/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 6986cab72f56..e59b5ea027ed 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -270,6 +270,9 @@ public abstract class BaseLockSettingsServiceTests { } protected void setSecureFrpMode(boolean secure) { + if (android.security.Flags.frpEnforcement()) { + mStorage.setTestFactoryResetProtectionState(secure); + } Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.SECURE_FRP_MODE, secure ? 1 : 0, UserHandle.USER_SYSTEM); } 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 ee076c6bcf4b..296d2cba83dd 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -21,12 +21,14 @@ import static org.mockito.Mockito.mock; import android.app.IActivityManager; import android.app.admin.DeviceStateCache; import android.content.Context; +import android.content.Intent; import android.content.pm.UserInfo; import android.hardware.authsecret.IAuthSecret; import android.os.Handler; import android.os.Parcel; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.os.storage.IStorageManager; import android.security.KeyStore; import android.security.keystore.KeyPermanentlyInvalidatedException; @@ -41,6 +43,9 @@ import com.android.server.pm.UserManagerInternal; import java.io.FileNotFoundException; public class LockSettingsServiceTestable extends LockSettingsService { + private Intent mSavedFrpNotificationIntent = null; + private UserHandle mSavedFrpNotificationUserHandle = null; + private String mSavedFrpNotificationPermission = null; public static class MockInjector extends LockSettingsService.Injector { @@ -218,4 +223,29 @@ public class LockSettingsServiceTestable extends LockSettingsService { mAuthSecret = null; } } + + @Override + void sendBroadcast(Intent intent, UserHandle userHandle, String permission) { + mSavedFrpNotificationIntent = intent; + mSavedFrpNotificationUserHandle = userHandle; + mSavedFrpNotificationPermission = permission; + } + + String getSavedFrpNotificationPermission() { + return mSavedFrpNotificationPermission; + } + + UserHandle getSavedFrpNotificationUserHandle() { + return mSavedFrpNotificationUserHandle; + } + + Intent getSavedFrpNotificationIntent() { + return mSavedFrpNotificationIntent; + } + + void clearRecordedFrpNotificationData() { + mSavedFrpNotificationIntent = null; + mSavedFrpNotificationPermission = null; + mSavedFrpNotificationUserHandle = null; + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 705359708bc7..4b22652a3f21 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION; import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; @@ -39,7 +40,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.content.Intent; import android.os.RemoteException; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.service.gatekeeper.GateKeeperResponse; @@ -239,6 +242,12 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test + public void testSetLockCredential_forPrimaryUser_sendsFrpNotification() throws Exception { + setCredential(PRIMARY_USER_ID, newPassword("password")); + checkRecordedFrpNotificationIntent(); + } + + @Test public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception { setCredential(PRIMARY_USER_ID, newPassword("password")); verify(mRecoverableKeyStoreManager) @@ -323,6 +332,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test + public void testClearLockCredential_sendsFrpNotification() throws Exception { + setCredential(PRIMARY_USER_ID, newPassword("password")); + checkRecordedFrpNotificationIntent(); + mService.clearRecordedFrpNotificationData(); + clearCredential(PRIMARY_USER_ID, newPassword("password")); + checkRecordedFrpNotificationIntent(); + } + + @Test public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials() throws Exception { final LockscreenCredential parentPassword = newPassword("parentPassword"); @@ -519,6 +537,23 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mService.setString(null, "value", 0); } + private void checkRecordedFrpNotificationIntent() { + if (android.security.Flags.frpEnforcement()) { + Intent savedNotificationIntent = mService.getSavedFrpNotificationIntent(); + assertNotNull(savedNotificationIntent); + UserHandle userHandle = mService.getSavedFrpNotificationUserHandle(); + assertEquals(userHandle, + UserHandle.of(mInjector.getUserManagerInternal().getMainUserId())); + + String permission = mService.getSavedFrpNotificationPermission(); + assertEquals(CONFIGURE_FACTORY_RESET_PROTECTION, permission); + } else { + assertNull(mService.getSavedFrpNotificationIntent()); + assertNull(mService.getSavedFrpNotificationUserHandle()); + assertNull(mService.getSavedFrpNotificationPermission()); + } + } + private void checkPasswordHistoryLength(int userId, int expectedLen) { String history = mService.getString(LockPatternUtils.PASSWORD_HISTORY_KEY, "", userId); String[] hashes = TextUtils.split(history, LockPatternUtils.PASSWORD_HISTORY_DELIMITER); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index fa3c7a4c4769..c01d0f644983 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -35,6 +35,7 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { public final File mStorageDir; public PersistentDataBlockManagerInternal mPersistentDataBlockManager; private byte[] mPersistentData; + private boolean mIsFactoryResetProtectionActive = false; public LockSettingsStorageTestable(Context context, File storageDir) { super(context); @@ -63,6 +64,10 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { }).when(mPersistentDataBlockManager).getFrpCredentialHandle(); } + void setTestFactoryResetProtectionState(boolean active) { + mIsFactoryResetProtectionActive = active; + } + @Override File getChildProfileLockFile(int userId) { return remapToStorageDir(super.getChildProfileLockFile(userId)); @@ -101,4 +106,9 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { mappedPath.getParentFile().mkdirs(); return mappedPath; } + + @Override + public boolean isFactoryResetProtectionActive() { + return mIsFactoryResetProtectionActive; + } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 4451cae8db42..5f2abc3285c9 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; +import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; @@ -58,9 +59,11 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP; import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; +import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; +import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.allowedReasonsToString; import static android.net.NetworkPolicyManager.blockedReasonsToString; import static android.net.NetworkPolicyManager.uidPoliciesToString; @@ -88,6 +91,7 @@ import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; +import static com.android.server.net.NetworkPolicyManagerService.UID_MSG_STATE_CHANGED; import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons; import static com.android.server.net.NetworkPolicyManagerService.normalizeTemplate; @@ -196,8 +200,6 @@ import com.android.server.LocalServices; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.usage.AppStandbyInternal; -import com.google.common.util.concurrent.AbstractFuture; - import libcore.io.Streams; import org.junit.After; @@ -241,10 +243,8 @@ import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -2247,6 +2247,123 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testUidObserverFiltersProcStateChanges() throws Exception { + int testProcStateSeq = 0; + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // First callback for uid. + callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Doesn't cross the background threshold. + callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Crosses the background threshold. + callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Doesn't cross the foreground threshold. + callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE + 1, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Crosses the foreground threshold. + callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Doesn't cross the top threshold. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE + 1, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Crosses the top threshold. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Doesn't cross any other threshold. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE - 1, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testUidObserverFiltersStaleChanges() throws Exception { + final int testProcStateSeq = 51; + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // First callback for uid. + callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE + 100, testProcStateSeq, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Stale callback because the procStateSeq is smaller. + callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE - 100, testProcStateSeq - 10, + PROCESS_CAPABILITY_NONE); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testUidObserverFiltersCapabilityChanges() throws Exception { + int testProcStateSeq = 0; + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // First callback for uid. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // The same process-state with one network capability added. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // The same process-state with another network capability added. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK + | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // The same process-state with all capabilities, but no change in network capabilities. + callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++, + PROCESS_CAPABILITY_ALL); + assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); @@ -2559,17 +2676,6 @@ public class NetworkPolicyManagerServiceTest { verify(mStatsManager).setDefaultGlobalAlert(anyLong()); } - private static class TestAbstractFuture<T> extends AbstractFuture<T> { - @Override - public T get() throws InterruptedException, ExecutionException { - try { - return get(5, TimeUnit.SECONDS); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - } - private static void assertTimeEquals(long expected, long actual) { if (expected != actual) { fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual)); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index d50affba1ea1..a0f2395f5203 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static android.content.pm.ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED; import static android.content.pm.ShortcutInfo.DISABLED_REASON_NOT_DISABLED; import static android.content.pm.ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH; -import static android.content.pm.ShortcutInfo.DISABLED_REASON_VERSION_LOWER; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyOrNull; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyStringOrNull; @@ -78,7 +77,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; -import android.content.pm.LauncherApps.PinItemRequest; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; @@ -4844,7 +4842,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { doReturn(expected != DISABLED_REASON_SIGNATURE_MISMATCH).when( mMockPackageManagerInternal).isDataRestoreSafe(any(byte[].class), anyString()); - assertEquals(expected, spi.canRestoreTo(mService, pi, anyVersionOk)); + assertEquals(expected, spi.canRestoreTo(mService, pi, true)); } public void testCanRestoreTo() { @@ -4872,7 +4870,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x"); checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x", "y"); checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x"); - checkCanRestoreTo(DISABLED_REASON_VERSION_LOWER, spi1, false, 9, true, "sig1"); // Any version okay. checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, true, 9, true, "sig1"); @@ -5983,14 +5980,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void testBackupAndRestore_publisherLowerVersion() { - prepareForBackupTest(); - - addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version - - checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_VERSION_LOWER); - } - public void testBackupAndRestore_publisherWrongSignature() { prepareForBackupTest(); @@ -6626,252 +6615,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - - /** - * Restored to a lower version with no manifest shortcuts. All shortcuts are now invisible, - * and all calls from the publisher should ignore them. - */ - public void testBackupAndRestore_disabledShortcutsAreIgnored() { - // Publish two manifest shortcuts. - addManifestShortcutResource( - new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - R.xml.shortcut_5_altalt); - updatePackageVersion(CALLING_PACKAGE_1, 1); - mService.mPackageMonitor.onReceive(mServiceContext, - genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); - - runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcutWithShortLabel("s1", "original-title"), - makeShortcut("s2"), makeShortcut("s3")))); - }); - - // Pin from launcher 1. - runWithCaller(LAUNCHER_1, USER_0, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("ms1", "ms2", "ms3", "ms4", "s1", "s2"), HANDLE_USER_0); - }); - - doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe( - any(byte[].class), anyString()); - - backupAndRestore(); - - // Lower the version and remove the manifest shortcuts. - addManifestShortcutResource( - new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - R.xml.shortcut_0); - addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version - - // When re-installing the app, the manifest shortcut should be re-published. - mService.mPackageMonitor.onReceive(mServiceContext, - genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); - mService.mPackageMonitor.onReceive(mServiceContext, - genPackageAddIntent(LAUNCHER_1, USER_0)); - - // No shortcuts should be visible to the publisher. - runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { - assertWith(getCallerVisibleShortcuts()) - .isEmpty(); - }); - - final Runnable checkAllDisabledForLauncher = () -> { - runWithCaller(LAUNCHER_1, USER_0, () -> { - assertWith(getShortcutAsLauncher(USER_0)) - .areAllPinned() - .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2") - .areAllDisabled() - .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER) - - .forShortcutWithId("s1", si -> { - assertEquals("original-title", si.getShortLabel()); - }) - .forShortcutWithId("ms1", si -> { - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title1 + "/en" - , si.getShortLabel()); - }) - .forShortcutWithId("ms2", si -> { - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title2 + "/en" - , si.getShortLabel()); - }) - .forShortcutWithId("ms3", si -> { - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title1 + "/en" - , si.getShortLabel()); - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title2 + "/en" - , si.getLongLabel()); - }) - .forShortcutWithId("ms4", si -> { - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title2 + "/en" - , si.getShortLabel()); - assertEquals("string-com.android.test.1-user:0-res:" - + R.string.shortcut_title2 + "/en" - , si.getLongLabel()); - }); - }); - }; - - checkAllDisabledForLauncher.run(); - - runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { - - makeCallerForeground(); // CALLING_PACKAGE_1 is now in the foreground. - - // All changing API calls should be ignored. - - getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2")); - checkAllDisabledForLauncher.run(); - - getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2")); - checkAllDisabledForLauncher.run(); - - getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2")); - checkAllDisabledForLauncher.run(); - - getManager().removeAllDynamicShortcuts(); - getManager().removeDynamicShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2")); - checkAllDisabledForLauncher.run(); - - getManager().updateShortcuts(list(makeShortcutWithShortLabel("s1", "new-title"))); - checkAllDisabledForLauncher.run(); - - - // Add a shortcut -- even though ms1 was immutable, it will succeed. - assertTrue(getManager().addDynamicShortcuts(list( - makeShortcutWithShortLabel("ms1", "original-title")))); - - runWithCaller(LAUNCHER_1, USER_0, () -> { - assertWith(getShortcutAsLauncher(USER_0)) - .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2") - - .selectByIds("ms1") - .areAllEnabled() - .areAllDynamic() - .areAllPinned() - .forAllShortcuts(si -> { - assertEquals("original-title", si.getShortLabel()); - }) - - // The rest still exist and disabled. - .revertToOriginalList() - .selectByIds("ms2", "ms3", "ms4", "s1", "s2") - .areAllDisabled() - .areAllPinned() - ; - }); - - assertTrue(getManager().setDynamicShortcuts(list( - makeShortcutWithShortLabel("ms2", "new-title-2")))); - - runWithCaller(LAUNCHER_1, USER_0, () -> { - assertWith(getShortcutAsLauncher(USER_0)) - .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2") - - .selectByIds("ms1") - .areAllEnabled() - .areAllNotDynamic() // ms1 was not in the list, so no longer dynamic. - .areAllPinned() - .areAllMutable() - .forAllShortcuts(si -> { - assertEquals("original-title", si.getShortLabel()); - }) - - .revertToOriginalList() - .selectByIds("ms2") - .areAllEnabled() - .areAllDynamic() - .areAllPinned() - .areAllMutable() - .forAllShortcuts(si -> { - assertEquals("new-title-2", si.getShortLabel()); - }) - - // The rest still exist and disabled. - .revertToOriginalList() - .selectByIds("ms3", "ms4", "s1", "s2") - .areAllDisabled() - .areAllPinned() - ; - }); - - // Prepare for requestPinShortcut(). - setDefaultLauncher(USER_0, LAUNCHER_1); - mPinConfirmActivityFetcher = (packageName, userId) -> - new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS); - - mManager.requestPinShortcut( - makeShortcutWithShortLabel("ms3", "new-title-3"), - /*PendingIntent=*/ null); - - // Note this was pinned, so it'll be accepted right away. - runWithCaller(LAUNCHER_1, USER_0, () -> { - assertWith(getShortcutAsLauncher(USER_0)) - .selectByIds("ms3") - .areAllEnabled() - .areAllNotDynamic() - .areAllPinned() - .areAllMutable() - .forAllShortcuts(si -> { - assertEquals("new-title-3", si.getShortLabel()); - // The new one replaces the old manifest shortcut, so the long label - // should be gone now. - assertNull(si.getLongLabel()); - }); - }); - - // Now, change the launcher to launcher2, and request pin again. - setDefaultLauncher(USER_0, LAUNCHER_2); - - reset(mServiceContext); - - assertTrue(mManager.isRequestPinShortcutSupported()); - mManager.requestPinShortcut( - makeShortcutWithShortLabel("ms4", "new-title-4"), - /*PendingIntent=*/ null); - - // Initially there should be no pinned shortcuts for L2. - runWithCaller(LAUNCHER_2, USER_0, () -> { - assertWith(getShortcutAsLauncher(USER_0)) - .selectPinned() - .isEmpty(); - - final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); - - verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0)); - - assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT, - intent.getValue().getAction()); - assertEquals(LAUNCHER_2, intent.getValue().getComponent().getPackageName()); - - // Check the request object. - final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue()); - - assertNotNull(request); - assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType()); - - assertWith(request.getShortcutInfo()) - .haveIds("ms4") - .areAllOrphan() - .forAllShortcuts(si -> { - assertEquals("new-title-4", si.getShortLabel()); - // The new one replaces the old manifest shortcut, so the long label - // should be gone now. - assertNull(si.getLongLabel()); - }); - assertTrue(request.accept()); - - assertWith(getShortcutAsLauncher(USER_0)) - .selectPinned() - .haveIds("ms4") - .areAllEnabled(); - }); - }); - } - /** * Test for restoring the pre-P backup format. */ 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 f9ba33b526a9..6e5c180de347 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -649,6 +649,74 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void testTotalSilence_consolidatedPolicyDisallowsAll() { + // Start with zen mode off just to make sure global/manual mode isn't doing anything. + mZenModeHelper.mZenMode = ZEN_MODE_OFF; + + // Create a zen rule that calls for total silence via zen mode, but does not specify any + // particular policy. This confirms that the application of the policy is based only on the + // actual zen mode setting. + AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_NONE) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + azr, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reason", Process.SYSTEM_UID); + + // Enable rule + mZenModeHelper.setAutomaticZenRuleState(ruleId, + new Condition(azr.getConditionId(), "", STATE_TRUE), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + Process.SYSTEM_UID); + + // Confirm that the consolidated policy doesn't allow anything + NotificationManager.Policy policy = mZenModeHelper.getConsolidatedNotificationPolicy(); + assertThat(policy.allowAlarms()).isFalse(); + assertThat(policy.allowMedia()).isFalse(); + assertThat(policy.allowCalls()).isFalse(); + assertThat(policy.allowMessages()).isFalse(); + assertThat(policy.allowConversations()).isFalse(); + assertThat(policy.allowEvents()).isFalse(); + assertThat(policy.allowReminders()).isFalse(); + assertThat(policy.allowRepeatCallers()).isFalse(); + assertThat(policy.allowPriorityChannels()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() { + // Start with zen mode off just to make sure global/manual mode isn't doing anything. + mZenModeHelper.mZenMode = ZEN_MODE_OFF; + + // Create a zen rule that calls for alarms only via zen mode, but does not specify any + // particular policy. This confirms that the application of the policy is based only on the + // actual zen mode setting. + AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + azr, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reason", Process.SYSTEM_UID); + + // Enable rule + mZenModeHelper.setAutomaticZenRuleState(ruleId, + new Condition(azr.getConditionId(), "", STATE_TRUE), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + Process.SYSTEM_UID); + + // Confirm that the consolidated policy allows only alarms and media and nothing else + NotificationManager.Policy policy = mZenModeHelper.getConsolidatedNotificationPolicy(); + assertThat(policy.allowAlarms()).isTrue(); + assertThat(policy.allowMedia()).isTrue(); + assertThat(policy.allowCalls()).isFalse(); + assertThat(policy.allowMessages()).isFalse(); + assertThat(policy.allowConversations()).isFalse(); + assertThat(policy.allowEvents()).isFalse(); + assertThat(policy.allowReminders()).isFalse(); + assertThat(policy.allowRepeatCallers()).isFalse(); + assertThat(policy.allowPriorityChannels()).isFalse(); + } + + @Test public void testZenUpgradeNotification() { /** * Commit a485ec65b5ba947d69158ad90905abf3310655cf disabled DND status change 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 31d6fa3e91f8..67c528cf40ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -769,7 +769,8 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setState(STOPPED, "Testing"); ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); - activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); + activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent(), + /* callerToken */ null); topActivity.finishing = true; doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null); @@ -1298,8 +1299,8 @@ public class ActivityRecordTests extends WindowTestsBase { targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */); waitUntilHandlersIdle(); - verify(resultToActivity).sendResult(anyInt(), eq(null), anyInt(), anyInt(), any(), eq(null), - anyBoolean()); + verify(resultToActivity).sendResult(anyInt(), eq(null), anyInt(), anyInt(), any(), any(), + eq(null), anyBoolean()); } /** 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 847c9d09b73a..6132ee3c89c4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1561,7 +1561,7 @@ public class ActivityStarterTests extends WindowTestsBase { .build(); final int result = starter.recycleTask(task, null, null, null, - BalVerdict.ALLOW_BY_DEFAULT); + BalVerdict.ALLOW_PRIVILEGED); assertThat(result == START_SUCCESS).isTrue(); assertThat(starter.mAddingToTask).isTrue(); } @@ -1857,7 +1857,7 @@ public class ActivityStarterTests extends WindowTestsBase { startActivityInner(starter, targetRecord, sourceRecord, null /* options */, null /* inTask */, null /* inTaskFragment */); verify(sourceRecord).sendResult(anyInt(), any(), anyInt(), eq(RESULT_CANCELED), any(), - any()); + any(), any()); } @Test @@ -2075,7 +2075,7 @@ public class ActivityStarterTests extends WindowTestsBase { starter.startActivityInner(target, source, null /* voiceSession */, null /* voiceInteractor */, 0 /* startFlags */, options, inTask, inTaskFragment, - BalVerdict.ALLOW_BY_DEFAULT, + BalVerdict.ALLOW_PRIVILEGED, null /* intentGrants */, -1 /* realCallingUid */); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index 203475156491..0b466b2b8a2c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -242,7 +242,8 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { verify(activity).getFilteredReferrer(eq(activity.launchedFromPackage)); activity.deliverNewIntentLocked(ActivityBuilder.DEFAULT_FAKE_UID, - new Intent(), null /* intentGrants */, "other.package2"); + new Intent(), null /* intentGrants */, "other.package2", + /* isShareIdentityEnabled */ false); verify(activity).getFilteredReferrer(eq("other.package2")); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index c404c77c8550..bb5887d12f4b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -187,7 +187,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { /* options */null, /* inTask */null, /* inTaskFragment */ null, - BalVerdict.ALLOW_BY_DEFAULT, + BalVerdict.ALLOW_PRIVILEGED, /* intentGrants */null, /* realCaiingUid */ -1); @@ -217,7 +217,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { /* options= */null, /* inTask= */null, /* inTaskFragment= */ null, - BalVerdict.ALLOW_BY_DEFAULT, + BalVerdict.ALLOW_PRIVILEGED, /* intentGrants= */null, /* realCaiingUid */ -1); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java index 06d30fc98c6a..29f48b86a375 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -58,7 +58,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { public void setUp() { super.setUp(); MockitoAnnotations.initMocks(this); - mCache = new TaskSnapshotCache(mWm, mLoader); + mCache = new TaskSnapshotCache(mLoader); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java index df5f3d149e88..7432537902a0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java @@ -61,7 +61,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas public void setUp() { super.setUp(); MockitoAnnotations.initMocks(this); - mCache = new TaskSnapshotCache(mWm, mLoader); + mCache = new TaskSnapshotCache(mLoader); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index f02dd3f70feb..cd3ce9192509 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsSource.ID_IME; import static android.view.Surface.ROTATION_0; @@ -55,7 +56,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 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.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW; @@ -1424,6 +1424,25 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(window2.isSecureLocked()); } + @Test + @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION) + public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() { + String testPackage = "test"; + int ownerId = 20; + final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId); + window.mAttrs.packageName = testPackage; + assertFalse(window.isSecureLocked()); + + PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId); + ArraySet<PackageInfo> blockedPackages = new ArraySet(); + blockedPackages.add(blockedPackage); + mWm.mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedPackages); + assertTrue(window.isSecureLocked()); + + mWm.mSensitiveContentPackages.removeBlockScreenCaptureForApps(blockedPackages); + assertFalse(window.isSecureLocked()); + } + private static class TestImeTargetChangeListener implements ImeTargetChangeListener { private IBinder mImeTargetToken; private boolean mIsRemoved; diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java index 8c6e101f2c03..afd34fc74e43 100644 --- a/telecomm/java/android/telecom/CallAttributes.java +++ b/telecomm/java/android/telecom/CallAttributes.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,6 +25,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.server.telecom.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -113,7 +116,8 @@ public final class CallAttributes implements Parcelable { public static final int VIDEO_CALL = 2; /** @hide */ - @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true) + @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER, + SUPPORTS_VIDEO_CALLING}, flag = true) @Retention(RetentionPolicy.SOURCE) public @interface CallCapability { } @@ -133,6 +137,12 @@ public final class CallAttributes implements Parcelable { * The call can be completely transferred from one endpoint to another. */ public static final int SUPPORTS_TRANSFER = 1 << 3; + /** + * The call supports video calling. This allows clients to gate video calling on a per call + * basis as opposed to re-registering the phone account. + */ + @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE) + public static final int SUPPORTS_VIDEO_CALLING = 1 << 4; /** * Build an instance of {@link CallAttributes}. In order to build a valid instance, a diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index a14078697c71..808a57589b47 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -293,12 +293,50 @@ public final class CallControl { try { mServerInterface.setMuteState(isMuted, new CallControlResultReceiver("requestMuteState", executor, callback)); - } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } + /** + * Request a new video state for the ongoing call. This can only be changed if the application + * has registered a {@link PhoneAccount} with the + * {@link PhoneAccount#CAPABILITY_SUPPORTS_VIDEO_CALLING} and set the + * {@link CallAttributes#SUPPORTS_VIDEO_CALLING} when adding the call via + * {@link TelecomManager#addCall(CallAttributes, Executor, OutcomeReceiver, + * CallControlCallback, CallEventCallback)} + * + * @param videoState to report to Telecom. To see the valid argument to pass, + * see {@link CallAttributes.CallType}. + * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback + * will be called on. + * @param callback that will be completed on the Telecom side that details success or failure + * of the requested operation. + * + * {@link OutcomeReceiver#onResult} will be called if Telecom has successfully + * switched the video state. + * + * {@link OutcomeReceiver#onError} will be called if Telecom has failed to set + * the new video state. A {@link CallException} will be passed + * that details why the operation failed. + * @throws IllegalArgumentException if the argument passed for videoState is invalid. To see a + * list of valid states, see {@link CallAttributes.CallType}. + */ + @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE) + public void requestVideoState(@CallAttributes.CallType int videoState, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<Void, CallException> callback) { + validateVideoState(videoState); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + try { + mServerInterface.requestVideoState(videoState, mCallId, + new CallControlResultReceiver("requestVideoState", executor, callback)); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** * Raises an event to the {@link android.telecom.InCallService} implementations tracking this * call via {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}. diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java index a41c0113e933..b0438bfa0289 100644 --- a/telecomm/java/android/telecom/CallEventCallback.java +++ b/telecomm/java/android/telecom/CallEventCallback.java @@ -16,9 +16,12 @@ package android.telecom; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.os.Bundle; +import com.android.server.telecom.flags.Flags; + import java.util.List; /** @@ -51,6 +54,14 @@ public interface CallEventCallback { void onMuteStateChanged(boolean isMuted); /** + * Called when the video state changes. + * + * @param videoState The current video state. + */ + @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE) + default void onVideoStateChanged(@CallAttributes.CallType int videoState) {} + + /** * Telecom is informing the client user requested call streaming but the stream can't be * started. * diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java index 467e89c78810..a2c60862f559 100644 --- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java +++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java @@ -33,6 +33,8 @@ import android.telecom.PhoneAccountHandle; import android.text.TextUtils; import android.util.Log; +import com.android.server.telecom.flags.Flags; + import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -148,6 +150,7 @@ public class ClientTransactionalServiceWrapper { private static final String ON_REQ_ENDPOINT_CHANGE = "onRequestEndpointChange"; private static final String ON_AVAILABLE_CALL_ENDPOINTS = "onAvailableCallEndpointsChanged"; private static final String ON_MUTE_STATE_CHANGED = "onMuteStateChanged"; + private static final String ON_VIDEO_STATE_CHANGED = "onVideoStateChanged"; private static final String ON_CALL_STREAMING_FAILED = "onCallStreamingFailed"; private static final String ON_EVENT = "onEvent"; @@ -261,6 +264,11 @@ public class ClientTransactionalServiceWrapper { handleEventCallback(callId, ON_MUTE_STATE_CHANGED, isMuted); } + @Override + public void onVideoStateChanged(String callId, int videoState) { + handleEventCallback(callId, ON_VIDEO_STATE_CHANGED, videoState); + } + public void handleEventCallback(String callId, String action, Object arg) { Log.d(TAG, TextUtils.formatSimple("hEC: [%s], callId=[%s]", action, callId)); // lookup the callEventCallback associated with the particular call @@ -281,6 +289,11 @@ public class ClientTransactionalServiceWrapper { case ON_MUTE_STATE_CHANGED: callback.onMuteStateChanged((boolean) arg); break; + case ON_VIDEO_STATE_CHANGED: + if (Flags.transactionalVideoState()) { + callback.onVideoStateChanged((int) arg); + } + break; case ON_CALL_STREAMING_FAILED: callback.onCallStreamingFailed((int) arg /* reason */); break; diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl index 372e4a12ff70..ac496607abe6 100644 --- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl @@ -34,4 +34,5 @@ oneway interface ICallControl { void requestCallEndpointChange(in CallEndpoint callEndpoint, in ResultReceiver callback); void setMuteState(boolean isMuted, in ResultReceiver callback); void sendEvent(String callId, String event, in Bundle extras); + void requestVideoState(int videoState, String callId, in ResultReceiver callback); }
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl index 213cafbbf188..e4d6b0c79c49 100644 --- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl @@ -45,6 +45,8 @@ oneway interface ICallEventCallback { void onCallEndpointChanged(String callId, in CallEndpoint endpoint); void onAvailableCallEndpointsChanged(String callId, in List<CallEndpoint> endpoint); void onMuteStateChanged(String callId, boolean isMuted); + // -- Video Related + void onVideoStateChanged(String callId, int videoState); // -- Events void onEvent(String callId, String event, in Bundle extras); // hidden methods that help with cleanup diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java index 4ff9712f0907..633694ac97da 100644 --- a/telephony/java/android/telephony/DomainSelectionService.java +++ b/telephony/java/android/telephony/DomainSelectionService.java @@ -20,6 +20,7 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; @@ -855,7 +856,8 @@ public abstract class DomainSelectionService extends Service { * * @return an {@link Executor} used to execute methods called remotely by the framework. */ - public @NonNull Executor onCreateExecutor() { + @SuppressLint("OnNameExpected") + public @NonNull Executor getCreateExecutor() { return Runnable::run; } @@ -869,7 +871,7 @@ public abstract class DomainSelectionService extends Service { public final @NonNull Executor getCachedExecutor() { synchronized (mExecutorLock) { if (mExecutor == null) { - Executor e = onCreateExecutor(); + Executor e = getCreateExecutor(); mExecutor = (e != null) ? e : Runnable::run; } return mExecutor; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index cd641b8be0b6..c5f2d42389e5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -4690,7 +4690,6 @@ public class SubscriptionManager { * @param subscriptionId the subId of the subscription * @param userHandle user handle of the user * @return {@code true} if subscription is associated with user - * {code true} if there are no subscriptions on device * else {@code false} if subscription is not associated with user. * * @throws IllegalArgumentException if subscription doesn't exist. @@ -4721,6 +4720,37 @@ public class SubscriptionManager { } /** + * Returns whether the given subscription is associated with the calling user. + * + * @param subscriptionId the subscription ID of the subscription + * @return {@code true} if the subscription is associated with the user that the current process + * is running in; {@code false} otherwise. + * + * @throws IllegalArgumentException if subscription doesn't exist. + * @throws SecurityException if the caller doesn't have permissions required. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @FlaggedApi(Flags.FLAG_SUBSCRIPTION_USER_ASSOCIATION_QUERY) + public boolean isSubscriptionAssociatedWithUser(int subscriptionId) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[isSubscriptionAssociatedWithCallingUser]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isSubscriptionAssociatedWithCallingUser(subscriptionId); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** * Get list of subscriptions associated with user. * * @param userHandle user handle of the user diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 041822bf4ee9..fd9aae9ff835 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -15011,6 +15011,27 @@ public class TelephonyManager { } /** + * Get the emergency assistance package name. + * + * @return the package name of the emergency assistance app. + * @throws IllegalStateException if emergency assistance is not enabled. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @FlaggedApi(android.permission.flags.Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED) + @NonNull + @SystemApi + public String getEmergencyAssistancePackage() { + if (!isEmergencyAssistanceEnabled()) { + throw new IllegalStateException("isEmergencyAssistanceEnabled() is false."); + } + String emergencyRole = mContext.getSystemService(RoleManager.class) + .getEmergencyRoleHolder(mContext.getUserId()); + return Objects.requireNonNull(emergencyRole, "Emergency role holder must not be null"); + } + + /** * Get the emergency number list based on current locale, sim, default, modem and network. * * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index cc770aa65888..6678f408e720 100644 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -332,12 +332,23 @@ interface ISub { UserHandle getSubscriptionUserHandle(int subId); /** + * Returns whether the given subscription is associated with the calling user. + * + * @param subscriptionId the subscription ID of the subscription + * @return {@code true} if the subscription is associated with the user that the current process + * is running in; {@code false} otherwise. + * + * @throws IllegalArgumentException if subscription doesn't exist. + * @throws SecurityException if the caller doesn't have permissions required. + */ + boolean isSubscriptionAssociatedWithCallingUser(int subscriptionId); + + /** * Check if subscription and user are associated with each other. * * @param subscriptionId the subId of the subscription * @param userHandle user handle of the user * @return {@code true} if subscription is associated with user - * {code true} if there are no subscriptions on device * else {@code false} if subscription is not associated with user. * * @throws IllegalArgumentException if subscription is invalid. diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml index c62db1ea5ca9..f602c5124e77 100644 --- a/tests/Input/AndroidTest.xml +++ b/tests/Input/AndroidTest.xml @@ -4,6 +4,7 @@ --> <configuration description="Runs Input Tests"> <option name="test-tag" value="InputTests" /> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on" /> @@ -22,4 +23,9 @@ <option name="test-timeout" value="600s" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> </test> + <object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController" + type="module_controller"> + <!-- Take screenshot upon test failure --> + <option name="screenshot-on-failure" value="true" /> + </object> </configuration> diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java index 14230fe4c323..0fb4f90f354f 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java @@ -42,6 +42,7 @@ import android.view.SurfaceControlViewHost; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.TextView; +import android.window.InputTransferToken; public class EmbeddedWindowService extends Service { private static final String TAG = "EmbeddedWindowService"; @@ -118,7 +119,7 @@ public class EmbeddedWindowService extends Service { @Override public void attachEmbeddedSurfaceControl(SurfaceControl parentSc, int displayId, - IBinder hostToken) { + InputTransferToken inputTransferToken) { mHandler.post(() -> { Paint paint = new Paint(); paint.setTextSize(40); @@ -134,7 +135,7 @@ public class EmbeddedWindowService extends Service { c.drawText("Remote", 250, 250, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, + wm.registerBatchedSurfaceControlInputReceiver(displayId, inputTransferToken, mSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-remote " + event); diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl index 6b65b40ef8c6..e81f5f81481a 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl @@ -20,10 +20,12 @@ import android.os.IBinder; import com.android.test.viewembed.IAttachEmbeddedWindowCallback; import android.view.WindowManager.LayoutParams; import android.view.SurfaceControl; +import android.window.InputTransferToken; interface IAttachEmbeddedWindow { void attachEmbedded(IBinder hostToken, int width, int height, in IAttachEmbeddedWindowCallback callback); void relayout(in LayoutParams lp); - oneway void attachEmbeddedSurfaceControl(in SurfaceControl parentSurfaceControl, int displayId, IBinder hostToken); + oneway void attachEmbeddedSurfaceControl(in SurfaceControl parentSurfaceControl, int displayId, + in InputTransferToken inputTransferToken); oneway void tearDownEmbeddedSurfaceControl(); }
\ No newline at end of file diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java index 7330ec14011b..e700bc2f3d21 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -139,7 +139,7 @@ public class SurfaceInputTestActivity extends Activity { surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), - attachedSurfaceControl.getHostToken(), mLocalSurfaceControl, + attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-sc " + event); return false; @@ -160,7 +160,8 @@ public class SurfaceInputTestActivity extends Activity { WindowManager wm = getSystemService(WindowManager.class); wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), - mLocalSurfaceView.getHostToken(), mLocalSurfaceView.getSurfaceControl(), + mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(), + mLocalSurfaceView.getSurfaceControl(), Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-local " + event); return false; @@ -210,7 +211,8 @@ public class SurfaceInputTestActivity extends Activity { } try { mIAttachEmbeddedWindow.attachEmbeddedSurfaceControl(mParentSurfaceControl, - getDisplayId(), mRemoteSurfaceView.getHostToken()); + getDisplayId(), + mRemoteSurfaceView.getRootSurfaceControl().getInputTransferToken()); } catch (RemoteException e) { Log.e(TAG, "Failed to load embedded SurfaceControl", e); } diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp index c6dd29ce7cc6..30333da5e86c 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -54,6 +54,7 @@ java_library { // This library is _not_ specific to Android APIs. java_library_host { name: "hoststubgen-helper-runtime", + defaults: ["ravenwood-internal-only-visibility-java"], srcs: [ "helper-runtime-src/**/*.java", ], @@ -64,11 +65,11 @@ java_library_host { "guava", ], jarjar_rules: "jarjar-rules.txt", - visibility: ["//visibility:public"], } java_library { name: "hoststubgen-helper-runtime.ravenwood", + defaults: ["ravenwood-internal-only-visibility-java"], srcs: [ "helper-runtime-src/**/*.java", ], @@ -79,7 +80,6 @@ java_library { "guava", ], jarjar_rules: "jarjar-rules.txt", - visibility: ["//visibility:public"], } // Host-side stub generator tool. @@ -152,34 +152,3 @@ genrule_defaults { "hoststubgen_dump.txt", ], } - -java_library_host { - name: "hoststubgen-helper-libcore-runtime", - srcs: [ - "helper-framework-runtime-src/libcore-fake/**/*.java", - ], - visibility: ["//visibility:private"], -} - -java_host_for_device { - name: "hoststubgen-helper-libcore-runtime.ravenwood", - libs: [ - "hoststubgen-helper-libcore-runtime", - ], - visibility: ["//visibility:private"], -} - -java_library { - name: "hoststubgen-helper-framework-runtime.ravenwood", - defaults: ["ravenwood-internal-only-visibility-java"], - srcs: [ - "helper-framework-runtime-src/framework/**/*.java", - ], - libs: [ - "hoststubgen-helper-runtime.ravenwood", - "framework-minus-apex.ravenwood", - ], - static_libs: [ - "hoststubgen-helper-libcore-runtime.ravenwood", - ], -} diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh deleted file mode 100755 index a6847ae97bae..000000000000 --- a/tools/hoststubgen/scripts/run-all-tests.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# 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. - -source "${0%/*}"/../common.sh - -# Move to the top directory of hoststubgen -cd .. - -ATEST_ARGS="--host" - -# These tests are known to pass. -READY_TEST_MODULES=( - hoststubgen-test-tiny-test - CtsUtilTestCasesRavenwood - CtsOsTestCasesRavenwood # This one uses native sustitution, so let's run it too. -) - -MUST_BUILD_MODULES=( - "${NOT_READY_TEST_MODULES[*]}" -) - -# First, build all the test / etc modules. This shouldn't fail. -run m "${MUST_BUILD_MODULES[@]}" - -# Run the hoststubgen unittests / etc -run atest $ATEST_ARGS hoststubgentest hoststubgen-invoke-test - -# Next, run the golden check. This should always pass too. -# The following scripts _should_ pass too, but they depend on the internal paths to soong generated -# files, and they may fail when something changes in the build system. -run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh - -run ./hoststubgen/test-tiny-framework/run-test-manually.sh -run atest $ATEST_ARGS tiny-framework-dump-test - -# This script is already broken on goog/master -# run ./scripts/build-framework-hostside-jars-without-genrules.sh - -# These tests should all pass. -run atest $ATEST_ARGS ${READY_TEST_MODULES[*]} - -echo ""${0##*/}" finished, with no failures. Ready to submit!" diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt index 3c55237ce443..ce856cd49614 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt @@ -25,6 +25,7 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.OutputStream +import java.time.LocalDateTime import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.jar.JarOutputStream @@ -42,6 +43,13 @@ object ProtoLogTool { return source.contains(protoLogSimpleClassName) } + private fun zipEntry(path: String): ZipEntry { + val entry = ZipEntry(path) + // Use a constant time to improve the cachability of build actions. + entry.timeLocal = LocalDateTime.of(2008, 1, 1, 0, 0, 0) + return entry + } + private fun processClasses(command: CommandOptions) { val groups = injector.readLogGroups( command.protoLogGroupsJarArg, @@ -77,7 +85,7 @@ object ProtoLogTool { } }.map { future -> val (path, outSrc) = future.get() - outJar.putNextEntry(ZipEntry(path)) + outJar.putNextEntry(zipEntry(path)) outJar.write(outSrc.toByteArray()) outJar.closeEntry() } @@ -90,7 +98,7 @@ object ProtoLogTool { val cachePackage = cacheSplit.dropLast(1).joinToString(".") val cachePath = "gen/${cacheSplit.joinToString("/")}.java" - outJar.putNextEntry(ZipEntry(cachePath)) + outJar.putNextEntry(zipEntry(cachePath)) outJar.write(generateLogGroupCache(cachePackage, cacheName, groups, command.protoLogImplClassNameArg, command.protoLogGroupsClassNameArg).toByteArray()) |