diff options
465 files changed, 9536 insertions, 3193 deletions
diff --git a/api/Android.bp b/api/Android.bp index a148cbd05632..9d2147c32760 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -361,7 +361,10 @@ stubs_defaults { previous_api: ":android.api.public.latest", merge_annotations_dirs: ["metalava-manual"], defaults_visibility: ["//frameworks/base/api"], - visibility: ["//frameworks/base/api"], + visibility: [ + "//frameworks/base/api", + "//frameworks/base/core/api", + ], } // We resolve dependencies on APIs in modules by depending on a prebuilt of the whole diff --git a/api/api.go b/api/api.go index fa2be21db09f..c733f5b5bffd 100644 --- a/api/api.go +++ b/api/api.go @@ -130,7 +130,7 @@ type MergedTxtDefinition struct { Scope string } -func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { +func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) { metalavaCmd := "$(location metalava)" // Silence reflection warnings. See b/168689341 metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " @@ -140,7 +140,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { if txt.Scope != "public" { filename = txt.Scope + "-" + filename } - moduleName := ctx.ModuleName() + "-" + filename + moduleName := ctx.ModuleName() + stubsTypeSuffix + filename props := genruleProps{} props.Name = proptools.StringPtr(moduleName) @@ -148,17 +148,19 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { props.Out = []string{filename} props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)") props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...) - props.Dists = []android.Dist{ - { - Targets: []string{"droidcore"}, - Dir: proptools.StringPtr("api"), - Dest: proptools.StringPtr(filename), - }, - { - Targets: []string{"api_txt", "sdk"}, - Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), - Dest: proptools.StringPtr(txt.DistFilename), - }, + if doDist { + props.Dists = []android.Dist{ + { + Targets: []string{"droidcore"}, + Dir: proptools.StringPtr("api"), + Dest: proptools.StringPtr(filename), + }, + { + Targets: []string{"api_txt", "sdk"}, + Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), + Dest: proptools.StringPtr(txt.DistFilename), + }, + } } props.Visibility = []string{"//visibility:public"} ctx.CreateModule(genrule.GenRuleFactory, &props) @@ -343,7 +345,7 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str ctx.CreateModule(android.FileGroupFactory, &props) } -func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { +func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string, baseTxtModulePrefix, stubsTypeSuffix string, doDist bool) { var textFiles []MergedTxtDefinition tagSuffix := []string{".api.txt}", ".removed-api.txt}"} @@ -352,7 +354,7 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, DistFilename: distFilename[i], - BaseTxt: ":non-updatable-" + f, + BaseTxt: ":" + baseTxtModulePrefix + f, Modules: bootclasspath, ModuleTag: "{.public" + tagSuffix[i], Scope: "public", @@ -360,7 +362,7 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-" + f, + BaseTxt: ":" + baseTxtModulePrefix + "system-" + f, Modules: bootclasspath, ModuleTag: "{.system" + tagSuffix[i], Scope: "system", @@ -368,7 +370,7 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, DistFilename: distFilename[i], - BaseTxt: ":non-updatable-module-lib-" + f, + BaseTxt: ":" + baseTxtModulePrefix + "module-lib-" + f, Modules: bootclasspath, ModuleTag: "{.module-lib" + tagSuffix[i], Scope: "module-lib", @@ -376,14 +378,14 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-server-" + f, + BaseTxt: ":" + baseTxtModulePrefix + "system-server-" + f, Modules: system_server_classpath, ModuleTag: "{.system-server" + tagSuffix[i], Scope: "system-server", }) } for _, txt := range textFiles { - createMergedTxt(ctx, txt) + createMergedTxt(ctx, txt, stubsTypeSuffix, doDist) } } @@ -465,7 +467,8 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...) sort.Strings(bootclasspath) } - createMergedTxts(ctx, bootclasspath, system_server_classpath) + createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false) + createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true) createMergedPublicStubs(ctx, bootclasspath) createMergedSystemStubs(ctx, bootclasspath) diff --git a/core/api/Android.bp b/core/api/Android.bp index 8d8a82b69b55..77594b758d19 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -96,3 +96,54 @@ filegroup { name: "non-updatable-test-lint-baseline.txt", srcs: ["test-lint-baseline.txt"], } + +// Exportable stub artifacts +filegroup { + name: "non-updatable-exportable-current.txt", + srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-removed.txt", + srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-system-current.txt", + srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-system-removed.txt", + srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-module-lib-current.txt", + srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-module-lib-removed.txt", + srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-test-current.txt", + srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-test-removed.txt", + srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-system-server-current.txt", + srcs: [":services-non-updatable-stubs{.exportable.api.txt}"], +} + +filegroup { + name: "non-updatable-exportable-system-server-removed.txt", + srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"], +} diff --git a/core/api/current.txt b/core/api/current.txt index bca15bd20657..f41982ff75bf 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1375,6 +1375,7 @@ package android { field public static final int reqTouchScreen = 16843303; // 0x1010227 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 field public static final int requestRawExternalStorageAccess = 16844357; // 0x1010645 + field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller; field public static final int requireDeviceScreenOn = 16844317; // 0x101061d field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e @@ -8070,8 +8071,8 @@ package android.app.admin { method public boolean isUsbDataSignalingEnabled(); method public boolean isUsingUnifiedPassword(@NonNull android.content.ComponentName); method @NonNull public java.util.List<android.os.UserHandle> listForegroundAffiliatedUsers(); - method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow(); - method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow(int); + method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow(); + method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow(int); method public int logoutUser(@NonNull android.content.ComponentName); method public void reboot(@NonNull android.content.ComponentName); method public void removeActiveAdmin(@NonNull android.content.ComponentName); @@ -40960,6 +40961,14 @@ package android.service.notification { } +package android.service.persistentdata { + + @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager { + method @FlaggedApi("android.security.frp_enforcement") public boolean isFactoryResetProtectionActive(); + } + +} + package android.service.quickaccesswallet { public interface GetWalletCardsCallback { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8057e188e13c..f30c8cf15dc1 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -113,6 +113,7 @@ package android { field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; + field @FlaggedApi("android.security.frp_enforcement") public static final String CONFIGURE_FACTORY_RESET_PROTECTION = "android.permission.CONFIGURE_FACTORY_RESET_PROTECTION"; field public static final String CONFIGURE_INTERACT_ACROSS_PROFILES = "android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"; field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL"; field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"; @@ -3151,16 +3152,38 @@ package android.app.wallpapereffectsgeneration { package android.app.wearable { + @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public final class WearableSensingDataRequest implements android.os.Parcelable { + method public int describeContents(); + method public int getDataSize(); + method public int getDataType(); + method public static int getMaxRequestSize(); + method public static int getRateLimit(); + method @NonNull public static java.time.Duration getRateLimitWindowSize(); + method @NonNull public android.os.PersistableBundle getRequestDetails(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.wearable.WearableSensingDataRequest> CREATOR; + } + + @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final class WearableSensingDataRequest.Builder { + ctor public WearableSensingDataRequest.Builder(int); + method @NonNull public android.app.wearable.WearableSensingDataRequest build(); + method @NonNull public android.app.wearable.WearableSensingDataRequest.Builder setRequestDetails(@NonNull android.os.PersistableBundle); + } + public class WearableSensingManager { + method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent); method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); field public static final int STATUS_ACCESS_DENIED = 5; // 0x5 field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7 field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3 field public static final int STATUS_SUCCESS = 1; // 0x1 field public static final int STATUS_UNKNOWN = 0; // 0x0 field public static final int STATUS_UNSUPPORTED = 2; // 0x2 + field @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; // 0x8 field @FlaggedApi("android.app.wearable.enable_unsupported_operation_status_code") public static final int STATUS_UNSUPPORTED_OPERATION = 6; // 0x6 field public static final int STATUS_WEARABLE_UNAVAILABLE = 4; // 0x4 } @@ -4414,8 +4437,9 @@ package android.credentials.selection { } @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable { + ctor public CancelSelectionRequest(@NonNull android.credentials.selection.RequestToken, boolean, @NonNull String); method public int describeContents(); - method @NonNull public String getAppPackageName(); + method @NonNull public String getPackageName(); method @NonNull public android.credentials.selection.RequestToken getRequestToken(); method public boolean shouldShowCancellationExplanation(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -4497,10 +4521,10 @@ package android.credentials.selection { @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable { method public int describeContents(); - method @NonNull public String getAppPackageName(); method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest(); method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds(); method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest(); + method @NonNull public String getPackageName(); method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds(); method @NonNull public android.credentials.selection.RequestToken getRequestToken(); method @NonNull public String getType(); @@ -4684,7 +4708,7 @@ package android.hardware.camera2 { package android.hardware.camera2.extension { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class AdvancedExtender { - ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") protected AdvancedExtender(@NonNull android.hardware.camera2.CameraManager); + ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public AdvancedExtender(@NonNull android.hardware.camera2.CameraManager); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(@NonNull String); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(@NonNull String); method @FlaggedApi("com.android.internal.camera.flags.camera_extensions_characteristics_get") @NonNull public abstract java.util.List<android.util.Pair<android.hardware.camera2.CameraCharacteristics.Key,java.lang.Object>> getAvailableCharacteristicsKeyValues(); @@ -4692,23 +4716,23 @@ package android.hardware.camera2.extension { method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.SessionProcessor getSessionProcessor(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedCaptureOutputResolutions(@NonNull String); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedPreviewOutputResolutions(@NonNull String); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void init(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void initialize(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract boolean isExtensionAvailable(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap); } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class CameraExtensionService extends android.app.Service { ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") protected CameraExtensionService(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.os.IBinder onBind(@Nullable android.content.Intent); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.AdvancedExtender onInitializeAdvancedExtension(int); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract boolean onRegisterClient(@NonNull android.os.IBinder); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onUnregisterClient(@NonNull android.os.IBinder); } @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, @Nullable android.util.Size); + 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.concert_mode") public int getImageFormat(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @Nullable public android.util.Size getSize(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @Nullable public android.view.Surface getSurface(); + 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(); } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public class CharacteristicsMap { @@ -4726,10 +4750,10 @@ package android.hardware.camera2.extension { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class RequestProcessor { method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void abortCaptures(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int setRepeating(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int setRepeating(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException; method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void stopRepeating(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submit(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submitBurst(@NonNull java.util.List<android.hardware.camera2.extension.RequestProcessor.Request>, @Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submit(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException; + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submitBurst(@NonNull java.util.List<android.hardware.camera2.extension.RequestProcessor.Request>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException; } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final class RequestProcessor.Request { @@ -4748,15 +4772,15 @@ package android.hardware.camera2.extension { } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class SessionProcessor { - ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") protected SessionProcessor(); + ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public SessionProcessor(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void deInitSession(@NonNull android.os.IBinder); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.ExtensionConfiguration initSession(@NonNull android.os.IBinder, @NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap, @NonNull android.hardware.camera2.extension.CameraOutputSurface, @NonNull android.hardware.camera2.extension.CameraOutputSurface); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onCaptureSessionEnd(); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onCaptureSessionStart(@NonNull android.hardware.camera2.extension.RequestProcessor, @NonNull String); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void setParameters(@NonNull android.hardware.camera2.CaptureRequest); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startCapture(@Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startRepeating(@Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startTrigger(@NonNull android.hardware.camera2.CaptureRequest, @Nullable java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startMultiFrameCapture(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startRepeating(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startTrigger(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void stopRepeating(); } @@ -6953,6 +6977,8 @@ package android.media { @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs(); + method public static long getDefaultFadeInDurationMillis(); + method public static long getDefaultFadeOutDurationMillis(); method public long getFadeInDelayForOffenders(); method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes); method public long getFadeInDurationForUsage(int); @@ -6978,7 +7004,6 @@ package android.media { field @NonNull public static final android.os.Parcelable.Creator<android.media.FadeManagerConfiguration> CREATOR; field public static final long DURATION_NOT_SET = 0L; // 0x0L field public static final int FADE_STATE_DISABLED = 0; // 0x0 - field public static final int FADE_STATE_ENABLED_AUTO = 2; // 0x2 field public static final int FADE_STATE_ENABLED_DEFAULT = 1; // 0x1 field public static final String TAG = "FadeManagerConfiguration"; field public static final int VOLUME_SHAPER_SYSTEM_FADE_ID = 2; // 0x2 @@ -6993,10 +7018,10 @@ package android.media { method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableContentType(int); method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableUid(int); method @NonNull public android.media.FadeManagerConfiguration build(); - method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsage(int); - method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes); - method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentType(int); - method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUid(int); + method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsages(); + method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(); + method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentTypes(); + method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUids(); method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long); method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long); method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long); @@ -11275,6 +11300,7 @@ package android.permission { public final class PermissionManager { method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public static int checkPermission(@NonNull String, @NonNull String, @NonNull String, int); method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String); method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String); method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource); @@ -11282,12 +11308,16 @@ package android.permission { method public void finishDataDelivery(@NonNull String, @NonNull android.content.AttributionSource); method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages(); method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages(); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public int getPermissionFlags(@NonNull String, @NonNull String, @NonNull String); method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull String); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull String, @Nullable String); method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public void updatePermissionFlags(@NonNull String, @NonNull String, @NonNull String, int, int); field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS"; field public static final String EXTRA_PERMISSION_USAGES = "android.permission.extra.PERMISSION_USAGES"; field public static final int PERMISSION_GRANTED = 0; // 0x0 @@ -12674,13 +12704,15 @@ package android.service.oemlock { package android.service.persistentdata { - public class PersistentDataBlockManager { + @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager { + method @FlaggedApi("android.security.frp_enforcement") @RequiresPermission(android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION) public boolean deactivateFactoryResetProtection(@NonNull byte[]); method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState(); method public long getMaximumDataBlockSize(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName(); method public byte[] read(); + method @FlaggedApi("android.security.frp_enforcement") public boolean setFactoryResetProtectionSecret(@NonNull byte[]); method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean); method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe(); method public int write(byte[]); @@ -13427,10 +13459,21 @@ package android.service.watchdog { package android.service.wearable { + @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public interface WearableSensingDataRequester { + method public void requestData(@NonNull android.app.wearable.WearableSensingDataRequest, @NonNull java.util.function.Consumer<java.lang.Integer>); + field public static final int STATUS_OBSERVER_CANCELLED = 2; // 0x2 + field public static final int STATUS_SUCCESS = 1; // 0x1 + field public static final int STATUS_TOO_FREQUENT = 4; // 0x4 + field public static final int STATUS_TOO_LARGE = 3; // 0x3 + field public static final int STATUS_UNKNOWN = 0; // 0x0 + } + public abstract class WearableSensingService extends android.app.Service { ctor public WearableSensingService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverRegistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>); method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>); method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -13855,7 +13898,7 @@ package android.telecom { method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); - method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean); + method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; @@ -14188,12 +14231,12 @@ package android.telephony { method @NonNull public android.telephony.DataThrottlingRequest.Builder setDataThrottlingAction(int); } - @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public class DomainSelectionService extends android.app.Service { + @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public abstract class DomainSelectionService extends android.app.Service { ctor public DomainSelectionService(); method public void onBarringInfoUpdated(int, int, @NonNull android.telephony.BarringInfo); - method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); + method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); method @NonNull public java.util.concurrent.Executor onCreateExecutor(); - method public void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback); + 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 field public static final int SCAN_TYPE_LIMITED_SERVICE = 1; // 0x1 @@ -14207,7 +14250,7 @@ package android.telephony { method @Nullable public android.net.Uri getAddress(); method @Nullable public String getCallId(); method public int getCsDisconnectCause(); - method @Nullable public android.telephony.EmergencyRegResult getEmergencyRegResult(); + method @Nullable public android.telephony.EmergencyRegistrationResult getEmergencyRegistrationResult(); method @Nullable public android.telephony.ims.ImsReasonInfo getPsDisconnectCause(); method public int getSelectorType(); method public int getSlotIndex(); @@ -14223,13 +14266,13 @@ package android.telephony { @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final class DomainSelectionService.SelectionAttributes.Builder { ctor public DomainSelectionService.SelectionAttributes.Builder(int, int, int); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes build(); - method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@NonNull android.net.Uri); - method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@NonNull String); + method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@Nullable android.net.Uri); + method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@Nullable String); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCsDisconnectCause(int); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergency(boolean); - method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegResult(@NonNull android.telephony.EmergencyRegResult); + method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegistrationResult(@Nullable android.telephony.EmergencyRegistrationResult); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setExitedFromAirplaneMode(boolean); - method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@NonNull android.telephony.ims.ImsReasonInfo); + method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@Nullable android.telephony.ims.ImsReasonInfo); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setTestEmergencyNumber(boolean); method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setVideoCall(boolean); } @@ -14239,7 +14282,7 @@ package android.telephony { method public void reselectDomain(@NonNull android.telephony.DomainSelectionService.SelectionAttributes); } - @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegResult implements android.os.Parcelable { + @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegistrationResult implements android.os.Parcelable { method public int describeContents(); method public int getAccessNetwork(); method @NonNull public String getCountryIso(); @@ -14252,7 +14295,7 @@ package android.telephony { method public boolean isEmcBearerSupported(); method public boolean isVopsSupported(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegResult> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegistrationResult> CREATOR; } public final class ImsiEncryptionInfo implements android.os.Parcelable { @@ -15324,7 +15367,7 @@ package android.telephony { @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public interface WwanSelectorCallback { method public void onDomainSelected(int, boolean); - method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegResult>); + method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegistrationResult>); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index bbd6bde60adf..fc095d482450 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1283,10 +1283,6 @@ package android.credentials.selection { field public static final int RESULT_CODE_DIALOG_USER_CANCELED = 0; // 0x0 } - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable { - ctor @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public CancelSelectionRequest(@NonNull android.os.IBinder, boolean, @NonNull String); - } - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable { ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @Nullable android.credentials.selection.Entry); method @Nullable public android.credentials.selection.Entry getRemoteEntry(); @@ -1883,6 +1879,7 @@ package android.media { method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest(); method @Nullable public static android.media.AudioHalVersionInfo getHalVersion(); method public static final int[] getPublicStreamTypes(); + method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes(); method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getRs2Value(); method public int getStreamMinVolumeInt(int); @@ -2056,6 +2053,10 @@ package android.media.audiofx { package android.media.audiopolicy { + public class AudioPolicy { + method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getMixes(); + } + public static class AudioPolicy.Builder { method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 084c71f47603..a8d183a1d6dd 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -5954,6 +5954,20 @@ public class ActivityManager { } /** + * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color + * palette readiness. + * @hide + */ + @RequiresPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY) + public void setThemeOverlayReady(boolean readiness) { + try { + getService().setThemeOverlayReady(readiness); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Resets the state of the {@link com.android.server.am.AppErrors} instance. * This is intended for use with CTS only. * @hide diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 232fc926a802..0ae2e01a45fa 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1258,4 +1258,11 @@ public abstract class ActivityManagerInternal { */ public abstract boolean clearApplicationUserData(String packageName, boolean keepState, boolean isRestore, IPackageDataObserver observer, int userId); + + /** + * Returns current state of {@link com.android.systemui.theme.ThemeOverlayController} color + * palette readiness. + * @hide + */ + public abstract boolean getThemeOverlayReadiness(); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index d8aded40df7b..3ec39b5145a7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -836,7 +836,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public int checkPermission(String permName, String pkgName) { - return PermissionManager.checkPackageNamePermission(permName, pkgName, + return getPermissionManager().checkPackageNamePermission(permName, pkgName, mContext.getDeviceId(), getUserId()); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b063d04655f5..ceeaf5dea7d3 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -17,7 +17,6 @@ package android.app; import android.app.ActivityManager; -import android.app.ActivityManager.PendingIntentInfo; import android.app.ActivityTaskManager; import android.app.ApplicationStartInfo; import android.app.ApplicationErrorReport; @@ -553,6 +552,14 @@ interface IActivityManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isTopOfTask(in IBinder token); void bootAnimationComplete(); + + /** + * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color + * palette readiness. + * @throws RemoteException + */ + void setThemeOverlayReady(boolean readiness); + @UnsupportedAppUsage void registerTaskStackListener(in ITaskStackListener listener); void unregisterTaskStackListener(in ITaskStackListener listener); diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index ce1d43d10c34..33e260f352aa 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -23,6 +23,7 @@ import android.accessibilityservice.IAccessibilityServiceClient; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Rect; @@ -363,7 +364,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.grantRuntimePermission(packageName, permission, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); } finally { Binder.restoreCallingIdentity(identity); } @@ -383,7 +384,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.revokeRuntimePermission(packageName, permission, - Context.DEVICE_ID_DEFAULT, userId, null); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId, null); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 1ef434633612..c8762c69eeca 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -18,6 +18,7 @@ package android.app.admin; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.LOCK_DEVICE; import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL; @@ -6415,10 +6416,10 @@ public class DevicePolicyManager { * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have - * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is - * true, then the method will return without completing any action. Before version - * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, - * regardless of the caller's permissions. + * the LOCK_DEVICE permission or the device must have the + * device admin feature; if neither is true, then the method will return without completing + * any action. Before version {@link android.os.Build.VERSION_CODES#R}, + * the device needed the device admin feature, regardless of the caller's permissions. * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. @@ -6438,7 +6439,8 @@ public class DevicePolicyManager { * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} */ - @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true) + @SuppressLint("RequiresPermission") + @RequiresPermission(value = LOCK_DEVICE, conditional = true) public void lockNow() { lockNow(0); } @@ -6449,14 +6451,13 @@ public class DevicePolicyManager { * <p> * This method secures the device in response to an urgent situation, such as a lost or stolen * device. After this method is called, the device must be unlocked using strong authentication - * (PIN, pattern, or password). This API is for use only by device admins and holders of the - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission. + * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have - * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is - * true, then the method will return without completing any action. Before version - * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, - * regardless of the caller's permissions. + * the LOCK_DEVICE permission or the device must have the + * device admin feature; if neither is true, then the method will return without completing any + * action. Before version {@link android.os.Build.VERSION_CODES#R}, the device needed the device + * admin feature, regardless of the caller's permissions. * <p> * A calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. @@ -6485,7 +6486,7 @@ public class DevicePolicyManager { * @param flags May be 0 or {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} and the does not hold - * the {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission, or + * the {@link android.Manifest.permission#LOCK_DEVICE} permission, or * the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is passed by an * application that is not a profile owner of a managed profile. * @throws IllegalArgumentException if the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is @@ -6494,7 +6495,7 @@ public class DevicePolicyManager { * flag is passed when {@link #getStorageEncryptionStatus} does not return * {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}. */ - @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true) + @RequiresPermission(value = LOCK_DEVICE, conditional = true) public void lockNow(@LockNowFlag int flags) { if (mService != null) { try { diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl index 9d55ce28c84e..3cbc8a21d2d8 100644 --- a/core/java/android/app/wearable/IWearableSensingManager.aidl +++ b/core/java/android/app/wearable/IWearableSensingManager.aidl @@ -16,6 +16,7 @@ package android.app.wearable; +import android.app.PendingIntent; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteCallback; @@ -33,4 +34,8 @@ interface IWearableSensingManager { void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)") void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)") + void registerDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)") + void unregisterDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback); }
\ No newline at end of file diff --git a/core/java/android/app/wearable/WearableSensingDataRequest.java b/core/java/android/app/wearable/WearableSensingDataRequest.java new file mode 100644 index 000000000000..9329b37be078 --- /dev/null +++ b/core/java/android/app/wearable/WearableSensingDataRequest.java @@ -0,0 +1,190 @@ +/* + * 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.wearable; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import java.time.Duration; + +/** + * Data class for a data request for wearable sensing. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) +@SystemApi +public final class WearableSensingDataRequest implements Parcelable { + private static final int MAX_REQUEST_SIZE = 200; + private static final Duration RATE_LIMIT_WINDOW_SIZE = Duration.ofMinutes(1); + private static final int RATE_LIMIT = 30; + + private final int mDataType; + @NonNull private final PersistableBundle mRequestDetails; + + private WearableSensingDataRequest(int dataType, @NonNull PersistableBundle requestDetails) { + mDataType = dataType; + mRequestDetails = requestDetails; + } + + /** Returns the data type this request is for. */ + public int getDataType() { + return mDataType; + } + + /** Returns the details for this request. */ + @NonNull + public PersistableBundle getRequestDetails() { + return mRequestDetails; + } + + /** Returns the data size of this object when it is parcelled. */ + public int getDataSize() { + Parcel parcel = Parcel.obtain(); + try { + writeToParcel(parcel, describeContents()); + return parcel.dataSize(); + } finally { + parcel.recycle(); + } + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDataType); + dest.writeTypedObject(mRequestDetails, flags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "WearableSensingDataRequest { " + + "dataType = " + + mDataType + + ", " + + "requestDetails = " + + mRequestDetails + + " }"; + } + + /** + * Returns a String representation of this data request that shows its contents. + * + * @hide + */ + public String toExpandedString() { + if (mRequestDetails != null) { + // Trigger unparcelling so that its individual fields will be listed in toString + boolean unused = + mRequestDetails.getBoolean( + "PlaceholderForWearableSensingDataRequest#toExpandedString()"); + } + return toString(); + } + + /** + * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}. + * + * @hide + */ + public static final String REQUEST_BUNDLE_KEY = + "android.app.wearable.WearableSensingDataRequestBundleKey"; + + /** + * The bundle key for the status callback for a data request, used in {@code + * RemoteCallback#sendResult}. + * + * @hide + */ + public static final String REQUEST_STATUS_CALLBACK_BUNDLE_KEY = + "android.app.wearable.WearableSensingDataRequestStatusCallbackBundleKey"; + + public static final @NonNull Parcelable.Creator<WearableSensingDataRequest> CREATOR = + new Parcelable.Creator<WearableSensingDataRequest>() { + @Override + public WearableSensingDataRequest[] newArray(int size) { + return new WearableSensingDataRequest[size]; + } + + @Override + public WearableSensingDataRequest createFromParcel(@NonNull Parcel in) { + int dataType = in.readInt(); + PersistableBundle requestDetails = + in.readTypedObject(PersistableBundle.CREATOR); + return new WearableSensingDataRequest(dataType, requestDetails); + } + }; + + /** + * Returns the maximum allowed size of a WearableSensingDataRequest when it is parcelled. + * Instances that exceed this size can be constructed, but will be rejected by the system when + * they leave the isolated WearableSensingService. + */ + public static int getMaxRequestSize() { + return MAX_REQUEST_SIZE; + } + + /** + * Returns the rolling time window used to perform rate limiting on data requests leaving the + * WearableSensingService. + */ + @NonNull + public static Duration getRateLimitWindowSize() { + return RATE_LIMIT_WINDOW_SIZE; + } + + /** + * Returns the number of data requests allowed to leave the WearableSensingService in each + * {@link #getRateLimitWindowSize()}. + */ + public static int getRateLimit() { + return RATE_LIMIT; + } + + /** A builder for WearableSensingDataRequest. */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + public static final class Builder { + private int mDataType; + private PersistableBundle mRequestDetails; + + public Builder(int dataType) { + mDataType = dataType; + } + + /** Sets the request details. */ + public @NonNull Builder setRequestDetails(@NonNull PersistableBundle requestDetails) { + mRequestDetails = requestDetails; + return this; + } + + /** Builds the WearableSensingDataRequest. */ + public @NonNull WearableSensingDataRequest build() { + if (mRequestDetails == null) { + mRequestDetails = PersistableBundle.EMPTY; + } + return new WearableSensingDataRequest(mDataType, mRequestDetails); + } + } +} diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java index 401d0b7b47fd..077f7b5c2f3e 100644 --- a/core/java/android/app/wearable/WearableSensingManager.java +++ b/core/java/android/app/wearable/WearableSensingManager.java @@ -25,9 +25,11 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEvent; import android.companion.CompanionDeviceManager; import android.content.Context; +import android.content.Intent; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; @@ -58,7 +60,6 @@ import java.util.function.Consumer; * * @hide */ - @SystemApi @SystemService(Context.WEARABLE_SENSING_SERVICE) public class WearableSensingManager { @@ -71,6 +72,14 @@ public class WearableSensingManager { public static final String STATUS_RESPONSE_BUNDLE_KEY = "android.app.wearable.WearableSensingStatusBundleKey"; + /** + * The Intent extra key for the data request in the Intent sent to the PendingIntent registered + * with {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}. + * + * @hide + */ + public static final String EXTRA_WEARABLE_SENSING_DATA_REQUEST = + "android.app.wearable.extra.WEARABLE_SENSING_DATA_REQUEST"; /** * An unknown status. @@ -107,6 +116,7 @@ public class WearableSensingManager { * The value of the status code that indicates the method called is not supported by the * implementation of {@link WearableSensingService}. */ + @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE) public static final int STATUS_UNSUPPORTED_OPERATION = 6; @@ -118,20 +128,42 @@ public class WearableSensingManager { @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API) public static final int STATUS_CHANNEL_ERROR = 7; + /** The value of the status code that indicates the provided data type is not supported. */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; + /** @hide */ - @IntDef(prefix = { "STATUS_" }, value = { - STATUS_UNKNOWN, - STATUS_SUCCESS, - STATUS_UNSUPPORTED, - STATUS_SERVICE_UNAVAILABLE, - STATUS_WEARABLE_UNAVAILABLE, - STATUS_ACCESS_DENIED, - STATUS_UNSUPPORTED_OPERATION, - STATUS_CHANNEL_ERROR - }) + @IntDef( + prefix = {"STATUS_"}, + value = { + STATUS_UNKNOWN, + STATUS_SUCCESS, + STATUS_UNSUPPORTED, + STATUS_SERVICE_UNAVAILABLE, + STATUS_WEARABLE_UNAVAILABLE, + STATUS_ACCESS_DENIED, + STATUS_UNSUPPORTED_OPERATION, + STATUS_CHANNEL_ERROR, + STATUS_UNSUPPORTED_DATA_TYPE + }) @Retention(RetentionPolicy.SOURCE) public @interface StatusCode {} + /** + * Retrieves a {@link WearableSensingDataRequest} from the Intent sent to the PendingIntent + * provided to {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}. + * + * @param intent The Intent received from the PendingIntent. + * @return The WearableSensingDataRequest in the provided Intent, or null if the Intent does not + * contain a WearableSensingDataRequest. + */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + @Nullable + public static WearableSensingDataRequest getDataRequestFromIntent(@NonNull Intent intent) { + return intent.getParcelableExtra( + EXTRA_WEARABLE_SENSING_DATA_REQUEST, WearableSensingDataRequest.class); + } + private final Context mContext; private final IWearableSensingManager mService; @@ -256,6 +288,99 @@ public class WearableSensingManager { } } + /** + * Registers a data request observer for the provided data type. + * + * <p>When data is requested, the provided {@code dataRequestPendingIntent} will be invoked. A + * {@link WearableSensingDataRequest} can be extracted from the Intent sent to {@code + * dataRequestPendingIntent} by calling {@link #getDataRequestFromIntent(Intent)}. The observer + * can then provide the requested data via {@link #provideData(PersistableBundle, SharedMemory, + * Executor, Consumer)}. + * + * <p>There is no limit to the number of observers registered for a data type. How they are + * handled depends on the implementation of WearableSensingService. + * + * <p>When the observer is no longer needed, {@link #unregisterDataRequestObserver(int, + * PendingIntent, Executor, Consumer)} should be called with the same {@code + * dataRequestPendingIntent}. It should be done regardless of the status code returned from + * {@code statusConsumer} in order to clean up housekeeping data for the {@code + * dataRequestPendingIntent} maintained by the system. + * + * <p>Example: + * + * <pre>{@code + * // Create a PendingIntent for MyDataRequestBroadcastReceiver + * Intent intent = + * new Intent(actionString).setClass(context, MyDataRequestBroadcastReceiver.class); + * PendingIntent pendingIntent = PendingIntent.getBroadcast( + * context, 0, intent, PendingIntent.FLAG_MUTABLE); + * + * // Register the PendingIntent as a data request observer + * wearableSensingManager.registerDataRequestObserver( + * dataType, pendingIntent, executor, statusConsumer); + * + * // Within MyDataRequestBroadcastReceiver, receive the broadcast Intent and extract the + * // WearableSensingDataRequest + * {@literal @}Override + * public void onReceive(Context context, Intent intent) { + * WearableSensingDataRequest dataRequest = + * WearableSensingManager.getDataRequestFromIntent(intent); + * // After parsing the dataRequest, provide the data + * wearableSensingManager.provideData(data, sharedMemory, executor, statusConsumer); + * } + * }</pre> + * + * @param dataType The data type to listen to. Values are defined by the application that + * implements {@link WearableSensingService}. + * @param dataRequestPendingIntent A mutable {@link PendingIntent} that will be invoked when + * data is requested. See {@link #getDataRequestFromIntent(Intent)}. Activities are not + * allowed to be launched using this PendingIntent. + * @param executor Executor on which to run the consumer callback. + * @param statusConsumer A consumer that handles the status code for the observer registration. + */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) + public void registerDataRequestObserver( + int dataType, + @NonNull PendingIntent dataRequestPendingIntent, + @NonNull @CallbackExecutor Executor executor, + @NonNull @StatusCode Consumer<Integer> statusConsumer) { + try { + RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); + mService.registerDataRequestObserver( + dataType, dataRequestPendingIntent, statusCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a previously registered data request observer. If the provided {@link + * PendingIntent} was not registered, or is already unregistered, the {@link + * WearableSensingService} will not be notified. + * + * @param dataType The data type the observer is for. + * @param dataRequestPendingIntent The observer to unregister. + * @param executor Executor on which to run the consumer callback. + * @param statusConsumer A consumer that handles the status code for the observer + * unregistration. + */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) + public void unregisterDataRequestObserver( + int dataType, + @NonNull PendingIntent dataRequestPendingIntent, + @NonNull @CallbackExecutor Executor executor, + @NonNull @StatusCode Consumer<Integer> statusConsumer) { + try { + RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer); + mService.unregisterDataRequestObserver( + dataType, dataRequestPendingIntent, statusCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private static RemoteCallback createStatusCallback( Executor executor, Consumer<Integer> statusConsumer) { return new RemoteCallback( diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig index 5e8bdb5a2997..b4f628ffc9b3 100644 --- a/core/java/android/app/wearable/flags.aconfig +++ b/core/java/android/app/wearable/flags.aconfig @@ -22,6 +22,13 @@ flag { } flag { + name: "enable_restart_wss_process" + namespace: "machine_learning" + description: "When this flag is on, the system will restart the WearableSensingService process before providing it with a new secure wearable connection." + bug: "301427767" +} + +flag { name: "enable_hotword_wearable_sensing_api" namespace: "machine_learning" description: "This flag enables the APIs related to hotword in WearableSensingManager and WearableSensingService." diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 9fe8af516694..a64ee5b38cc1 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -239,6 +239,110 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public String requiredDisplayCategory; /** + * Constant corresponding to {@code none} in the + * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute. + * @hide + */ + public static final int CONTENT_URI_PERMISSION_NONE = 0; + + /** + * Constant corresponding to {@code read} in the + * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute. + * @hide + */ + public static final int CONTENT_URI_PERMISSION_READ = 1; + + /** + * Constant corresponding to {@code write} in the + * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute. + * @hide + */ + public static final int CONTENT_URI_PERMISSION_WRITE = 2; + + /** + * Constant corresponding to {@code readOrWrite} in the + * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute. + * @hide + */ + public static final int CONTENT_URI_PERMISSION_READ_OR_WRITE = 3; + + /** + * Constant corresponding to {@code readAndWrite} in the + * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute. + * @hide + */ + public static final int CONTENT_URI_PERMISSION_READ_AND_WRITE = 4; + + /** @hide */ + @SuppressWarnings("SwitchIntDef") + public static boolean isRequiredContentUriPermissionRead( + @RequiredContentUriPermission int permission) { + return switch (permission) { + case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE, + CONTENT_URI_PERMISSION_READ -> true; + default -> false; + }; + } + + /** @hide */ + @SuppressWarnings("SwitchIntDef") + public static boolean isRequiredContentUriPermissionWrite( + @RequiredContentUriPermission int permission) { + return switch (permission) { + case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE, + CONTENT_URI_PERMISSION_WRITE -> true; + default -> false; + }; + } + + /** @hide */ + @IntDef(prefix = "CONTENT_URI_PERMISSION_", value = { + CONTENT_URI_PERMISSION_NONE, + CONTENT_URI_PERMISSION_READ, + CONTENT_URI_PERMISSION_WRITE, + CONTENT_URI_PERMISSION_READ_OR_WRITE, + CONTENT_URI_PERMISSION_READ_AND_WRITE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RequiredContentUriPermission { + } + + private String requiredContentUriPermissionToFullString( + @RequiredContentUriPermission int permission) { + return switch (permission) { + case CONTENT_URI_PERMISSION_NONE -> "CONTENT_URI_PERMISSION_NONE"; + case CONTENT_URI_PERMISSION_READ -> "CONTENT_URI_PERMISSION_READ"; + case CONTENT_URI_PERMISSION_WRITE -> "CONTENT_URI_PERMISSION_WRITE"; + case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "CONTENT_URI_PERMISSION_READ_OR_WRITE"; + case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "CONTENT_URI_PERMISSION_READ_AND_WRITE"; + default -> "unknown=" + permission; + }; + } + + /** @hide */ + public static String requiredContentUriPermissionToShortString( + @RequiredContentUriPermission int permission) { + return switch (permission) { + case CONTENT_URI_PERMISSION_NONE -> "none"; + case CONTENT_URI_PERMISSION_READ -> "read"; + case CONTENT_URI_PERMISSION_WRITE -> "write"; + case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "read or write"; + case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "read and write"; + default -> "unknown=" + permission; + }; + } + + /** + * Specifies permissions necessary to launch this activity via + * {@link android.content.Context#startActivity} when passing content URIs. The default value is + * {@code none}, meaning no specific permissions are required. Setting this attribute restricts + * activity invocation based on the invoker's permissions. + * @hide + */ + @RequiredContentUriPermission + public int requireContentUriPermissionFromCaller; + + /** * Activity can not be resized and always occupies the fullscreen area with all windows fully * visible. * @hide @@ -1590,6 +1694,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { mMinAspectRatio = orig.mMinAspectRatio; supportsSizeChanges = orig.supportsSizeChanges; requiredDisplayCategory = orig.requiredDisplayCategory; + requireContentUriPermissionFromCaller = orig.requireContentUriPermissionFromCaller; } /** @@ -1946,6 +2051,11 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (requiredDisplayCategory != null) { pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory); } + if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) { + pw.println(prefix + "requireContentUriPermissionFromCaller=" + + requiredContentUriPermissionToFullString( + requireContentUriPermissionFromCaller)); + } super.dumpBack(pw, prefix, dumpFlags); } @@ -1993,6 +2103,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeBoolean(supportsSizeChanges); sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags); dest.writeString8(requiredDisplayCategory); + dest.writeInt(requireContentUriPermissionFromCaller); } /** @@ -2119,6 +2230,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { mKnownActivityEmbeddingCerts = null; } requiredDisplayCategory = source.readString8(); + requireContentUriPermissionFromCaller = source.readInt(); } /** diff --git a/core/java/android/credentials/selection/CancelSelectionRequest.java b/core/java/android/credentials/selection/CancelSelectionRequest.java index 2662d761c1c0..55acfdbddf94 100644 --- a/core/java/android/credentials/selection/CancelSelectionRequest.java +++ b/core/java/android/credentials/selection/CancelSelectionRequest.java @@ -21,7 +21,6 @@ import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENAB import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -59,7 +58,7 @@ public final class CancelSelectionRequest implements Parcelable { private final boolean mShouldShowCancellationExplanation; @NonNull - private final String mAppPackageName; + private final String mPackageName; /** * Returns the request token matching the user request that should be cancelled. @@ -85,8 +84,8 @@ public final class CancelSelectionRequest implements Parcelable { * metadata (e.g. "Cancelled by `App Name`"). */ @NonNull - public String getAppPackageName() { - return mAppPackageName; + public String getPackageName() { + return mPackageName; } /** @@ -98,33 +97,36 @@ public final class CancelSelectionRequest implements Parcelable { return mShouldShowCancellationExplanation; } + /** * Constructs a {@link CancelSelectionRequest}. * - * @hide + * @param requestToken request token matching the app request that should be cancelled + * @param shouldShowCancellationExplanation whether the UI should display some informational + * cancellation message before closing + * @param packageName package that is invoking this request + * */ - @TestApi - @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) - public CancelSelectionRequest(@NonNull IBinder token, boolean shouldShowCancellationExplanation, - @NonNull String appPackageName) { - mToken = token; + public CancelSelectionRequest(@NonNull RequestToken requestToken, + boolean shouldShowCancellationExplanation, @NonNull String packageName) { + mToken = requestToken.getToken(); mShouldShowCancellationExplanation = shouldShowCancellationExplanation; - mAppPackageName = appPackageName; + mPackageName = packageName; } private CancelSelectionRequest(@NonNull Parcel in) { mToken = in.readStrongBinder(); AnnotationValidations.validate(NonNull.class, null, mToken); mShouldShowCancellationExplanation = in.readBoolean(); - mAppPackageName = in.readString8(); - AnnotationValidations.validate(NonNull.class, null, mAppPackageName); + mPackageName = in.readString8(); + AnnotationValidations.validate(NonNull.class, null, mPackageName); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mToken); dest.writeBoolean(mShouldShowCancellationExplanation); - dest.writeString8(mAppPackageName); + dest.writeString8(mPackageName); } @Override diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java index 1837976f5d1f..ac2bae495219 100644 --- a/core/java/android/credentials/selection/IntentFactory.java +++ b/core/java/android/credentials/selection/IntentFactory.java @@ -210,7 +210,8 @@ public class IntentFactory { .config_credentialManagerDialogComponent)); intent.setComponent(componentName); intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, - new CancelSelectionRequest(requestToken, shouldShowCancellationUi, appPackageName)); + new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi, + appPackageName)); return intent; } diff --git a/core/java/android/credentials/selection/RequestInfo.java b/core/java/android/credentials/selection/RequestInfo.java index 60bbae683680..16d0802709f3 100644 --- a/core/java/android/credentials/selection/RequestInfo.java +++ b/core/java/android/credentials/selection/RequestInfo.java @@ -106,7 +106,7 @@ public final class RequestInfo implements Parcelable { private final String mType; @NonNull - private final String mAppPackageName; + private final String mPackageName; private final boolean mHasPermissionToOverrideDefault; @@ -172,8 +172,8 @@ public final class RequestInfo implements Parcelable { /** Returns the display name of the app that made this request. */ @NonNull - public String getAppPackageName() { - return mAppPackageName; + public String getPackageName() { + return mPackageName; } /** @@ -248,7 +248,7 @@ public final class RequestInfo implements Parcelable { boolean isShowAllOptionsRequested) { mToken = token; mType = type; - mAppPackageName = appPackageName; + mPackageName = appPackageName; mCreateCredentialRequest = createCredentialRequest; mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault; @@ -270,8 +270,8 @@ public final class RequestInfo implements Parcelable { AnnotationValidations.validate(NonNull.class, null, mToken); mType = type; AnnotationValidations.validate(NonNull.class, null, mType); - mAppPackageName = appPackageName; - AnnotationValidations.validate(NonNull.class, null, mAppPackageName); + mPackageName = appPackageName; + AnnotationValidations.validate(NonNull.class, null, mPackageName); mCreateCredentialRequest = createCredentialRequest; mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = in.readBoolean(); @@ -284,7 +284,7 @@ public final class RequestInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mToken); dest.writeString8(mType); - dest.writeString8(mAppPackageName); + dest.writeString8(mPackageName); dest.writeTypedObject(mCreateCredentialRequest, flags); dest.writeTypedObject(mGetCredentialRequest, flags); dest.writeBoolean(mHasPermissionToOverrideDefault); diff --git a/core/java/android/credentials/selection/RequestToken.java b/core/java/android/credentials/selection/RequestToken.java index 27b83f873732..f1953ced6202 100644 --- a/core/java/android/credentials/selection/RequestToken.java +++ b/core/java/android/credentials/selection/RequestToken.java @@ -30,6 +30,11 @@ import android.os.IBinder; * To compare if two requests pertain to the same session, compare their RequestTokens using * the {@link RequestToken#equals(Object)} method. * + * For example, when receiving a {@link android.credentials.selection.CancelSelectionRequest}, + * the developer should use {@link RequestToken#getToken()} to retrieve the token from request and + * compare whether it is equal with the cached token using {@link RequestToken#equals(Object)}. Only + * cancel the request when two tokens are the same. + * * @hide */ @SystemApi @@ -39,6 +44,12 @@ public final class RequestToken { @NonNull private final IBinder mToken; + /** @hide **/ + @NonNull + public IBinder getToken() { + return mToken; + } + /** @hide */ @TestApi @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) diff --git a/core/java/android/hardware/camera2/extension/AdvancedExtender.java b/core/java/android/hardware/camera2/extension/AdvancedExtender.java index 665357726ebf..4895f38d7328 100644 --- a/core/java/android/hardware/camera2/extension/AdvancedExtender.java +++ b/core/java/android/hardware/camera2/extension/AdvancedExtender.java @@ -43,8 +43,9 @@ import java.util.Map; * * <p>This advanced contract empowers implementations to gain access to * more Camera2 capability. This includes: (1) Add custom surfaces with - * specific formats like YUV, RAW, RAW_DEPTH. (2) Access to - * the capture request callbacks as well as all the images retrieved of + * specific formats like {@link android.graphics.ImageFormat#YUV_420_888}, + * {@link android.graphics.ImageFormat#RAW10}, {@link android.graphics.ImageFormat#RAW_DEPTH10}. + * (2) Access to the capture request callbacks as well as all the images retrieved of * various image formats. (3) * Able to triggers single or repeating request with the capabilities to * specify target surfaces, template id and parameters. @@ -60,8 +61,14 @@ public abstract class AdvancedExtender { private CameraUsageTracker mCameraUsageTracker; private static final String TAG = "AdvancedExtender"; + + /** + * Initialize a camera extension advanced extender instance. + * + * @param cameraManager the system camera manager + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - protected AdvancedExtender(@NonNull CameraManager cameraManager) { + public AdvancedExtender(@NonNull CameraManager cameraManager) { mCameraManager = cameraManager; try { String [] cameraIds = mCameraManager.getCameraIdListNoLazy(); @@ -87,6 +94,14 @@ public abstract class AdvancedExtender { mCameraUsageTracker = tracker; } + /** + * Returns the camera metadata vendor id, that can be used to + * configure and enable vendor tag support for a particular + * camera metadata buffer. + * + * @param cameraId The camera2 id string of the camera. + * @return the camera metadata vendor Id associated with the given camera + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) public long getMetadataVendorId(@NonNull String cameraId) { long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ? @@ -131,12 +146,15 @@ public abstract class AdvancedExtender { * CameraCharacteristics. */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public abstract void init(@NonNull String cameraId, @NonNull CharacteristicsMap map); + public abstract void initialize(@NonNull String cameraId, @NonNull CharacteristicsMap map); /** * Returns supported output format/size map for preview. The format - * could be PRIVATE or YUV_420_888. Implementations must support - * PRIVATE format at least. + * could be {@link android.graphics.ImageFormat#PRIVATE} or + * {@link android.graphics.ImageFormat#YUV_420_888}. Implementations must support + * {@link android.graphics.ImageFormat#PRIVATE} format at least. + * An example of how the map is parsed can be found in + * {@link #initializeParcelable(Map)} * * <p>The preview surface format in the CameraCaptureSession may not * be identical to the supported preview output format returned here. @@ -149,11 +167,16 @@ public abstract class AdvancedExtender { /** * Returns supported output format/size map for image capture. OEM is - * required to support both JPEG and YUV_420_888 format output. + * required to support both {@link android.graphics.ImageFormat#JPEG} and + * {@link android.graphics.ImageFormat#YUV_420_888} format output. + * An example of how the map is parsed can be found in + * {@link #initializeParcelable(Map)} * * <p>The surface created with this supported * format/size could be either added in CameraCaptureSession with HAL - * processing OR it configures intermediate surfaces(YUV/RAW..) and + * processing OR it configures intermediate surfaces( + * {@link android.graphics.ImageFormat#YUV_420_888}/ + * {@link android.graphics.ImageFormat#RAW10}..) and * writes the output to the output surface. * @param cameraId The camera2 id string of the camera. */ @@ -256,7 +279,7 @@ public abstract class AdvancedExtender { @Override public void init(String cameraId, Map<String, CameraMetadataNative> charsMapNative) { - AdvancedExtender.this.init(cameraId, new CharacteristicsMap(charsMapNative)); + AdvancedExtender.this.initialize(cameraId, new CharacteristicsMap(charsMapNative)); } @Override diff --git a/core/java/android/hardware/camera2/extension/CameraExtensionService.java b/core/java/android/hardware/camera2/extension/CameraExtensionService.java index fa0d14a3f05a..01698d54150c 100644 --- a/core/java/android/hardware/camera2/extension/CameraExtensionService.java +++ b/core/java/android/hardware/camera2/extension/CameraExtensionService.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi; import android.app.AppOpsManager; import android.app.Service; import android.content.Intent; +import android.hardware.camera2.CameraExtensionCharacteristics.Extension; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -92,7 +93,7 @@ public abstract class CameraExtensionService extends Service { @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override @NonNull - public IBinder onBind(@Nullable Intent intent) { + public final IBinder onBind(@Nullable Intent intent) { if (mCameraUsageTracker == null) { mCameraUsageTracker = new CameraTracker(); } @@ -153,21 +154,21 @@ public abstract class CameraExtensionService extends Service { } @Override - public IPreviewExtenderImpl initializePreviewExtension(int extensionType) + public IPreviewExtenderImpl initializePreviewExtension(@Extension int extensionType) throws RemoteException { // Basic Extension API is not supported return null; } @Override - public IImageCaptureExtenderImpl initializeImageExtension(int extensionType) + public IImageCaptureExtenderImpl initializeImageExtension(@Extension int extensionType) throws RemoteException { // Basic Extension API is not supported return null; } @Override - public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) + public IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) throws RemoteException { AdvancedExtender extender = CameraExtensionService.this.onInitializeAdvancedExtension( extensionType); @@ -205,5 +206,5 @@ public abstract class CameraExtensionService extends Service { */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) @NonNull - public abstract AdvancedExtender onInitializeAdvancedExtension(int extensionType); + public abstract AdvancedExtender onInitializeAdvancedExtension(@Extension int extensionType); } diff --git a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java index f98ebee5d7c7..b4fe7fe1f0d1 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java +++ b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java @@ -18,8 +18,8 @@ package android.hardware.camera2.extension; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; +import android.graphics.ImageFormat; import android.hardware.camera2.utils.SurfaceUtils; import android.util.Size; import android.view.Surface; @@ -28,6 +28,14 @@ import com.android.internal.camera.flags.Flags; /** + * Helper method used to describe a single camera output + * {@link Surface}. + * + * <p>Instances of this class can be used as arguments when + * initializing {@link ExtensionOutputConfiguration}.</p> + * + * @see ExtensionConfiguration + * @see ExtensionOutputConfiguration * @hide */ @SystemApi @@ -40,27 +48,39 @@ public final class CameraOutputSurface { mOutputSurface = surface; } + /** + * Initialize a camera output surface instance + * + * @param surface Output {@link Surface} to be + * configured as camera output + * @param size Requested size of the camera + * output + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) public CameraOutputSurface(@NonNull Surface surface, - @Nullable Size size ) { + @NonNull Size size) { mOutputSurface = new OutputSurface(); mOutputSurface.surface = surface; mOutputSurface.imageFormat = SurfaceUtils.getSurfaceFormat(surface); - if (size != null) { - mOutputSurface.size = new android.hardware.camera2.extension.Size(); - mOutputSurface.size.width = size.getWidth(); - mOutputSurface.size.height = size.getHeight(); - } + mOutputSurface.size = new android.hardware.camera2.extension.Size(); + mOutputSurface.size.width = size.getWidth(); + mOutputSurface.size.height = size.getHeight(); } + /** + * Return the current output {@link Surface} + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - @Nullable + @NonNull public Surface getSurface() { return mOutputSurface.surface; } + /** + * Return the current requested output size + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - @Nullable + @NonNull public android.util.Size getSize() { if (mOutputSurface.size != null) { return new Size(mOutputSurface.size.width, mOutputSurface.size.height); @@ -68,8 +88,11 @@ public final class CameraOutputSurface { return null; } + /** + * Return the current surface output {@link android.graphics.ImageFormat} + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public int getImageFormat() { + public @ImageFormat.Format int getImageFormat() { return mOutputSurface.imageFormat; } } diff --git a/core/java/android/hardware/camera2/extension/CharacteristicsMap.java b/core/java/android/hardware/camera2/extension/CharacteristicsMap.java index af83595babef..495abc8100ae 100644 --- a/core/java/android/hardware/camera2/extension/CharacteristicsMap.java +++ b/core/java/android/hardware/camera2/extension/CharacteristicsMap.java @@ -30,12 +30,22 @@ import java.util.Map; import java.util.Set; /** + * Helper class used to forward the current + * system camera characteristics information. + * * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_CONCERT_MODE) public class CharacteristicsMap { private final HashMap<String, CameraCharacteristics> mCharMap; + + /** + * Initialize a camera characteristics map instance + * + * @param charsMap Maps camera ids to respective + * {@link CameraCharacteristics} + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) CharacteristicsMap(@NonNull Map<String, CameraMetadataNative> charsMap) { mCharMap = new HashMap<>(); @@ -44,12 +54,26 @@ public class CharacteristicsMap { } } + /** + * Return the set of camera ids stored in the characteristics map + * + * @return Set of the camera ids stored in the map + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) @NonNull public Set<String> getCameraIds() { return mCharMap.keySet(); } + /** + * Return the corresponding {@link CameraCharacteristics} given + * a valid camera id + * + * @param cameraId Camera device id + * + * @return Valid {@link CameraCharacteristics} instance of null + * in case the camera id is not part of the map + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Nullable public CameraCharacteristics get(@NonNull String cameraId) { diff --git a/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java b/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java index 2d9ab765d1e0..96c88e660e10 100644 --- a/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java +++ b/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java @@ -20,7 +20,9 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; +import android.os.IBinder; import com.android.internal.camera.flags.Flags; @@ -28,6 +30,15 @@ import java.util.ArrayList; import java.util.List; /** + * Helper class used to guide the camera framework when + * initializing the internal camera capture session. + * It contains all required internal outputs, parameters, + * modes and settings. + * + * <p>Extension must decide the final set of output surfaces + * and pass an instance of ExtensionConfiguration as part + * of the result during calls to {@link SessionProcessor#initSession}.</p> + * * @hide */ @SystemApi @@ -38,9 +49,25 @@ public class ExtensionConfiguration { private final List<ExtensionOutputConfiguration> mOutputs; private final CaptureRequest mSessionParameters; + /** + * Initialize an extension configuration instance + * + * @param sessionType The type of camera capture session + * operating mode to be used + * @param sessionTemplateId The request template id to be used + * for generating the session parameter + * capture request + * @param outputs List of {@link ExtensionOutputConfiguration} + * camera outputs to be configured + * as part of the capture session + * @param sessionParams An optional set of camera capture + * session parameter values + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public ExtensionConfiguration(int sessionType, int sessionTemplateId, @NonNull - List<ExtensionOutputConfiguration> outputs, @Nullable CaptureRequest sessionParams) { + public ExtensionConfiguration(@CameraDevice.SessionOperatingMode int sessionType, + @CameraDevice.RequestTemplate int sessionTemplateId, + @NonNull List<ExtensionOutputConfiguration> outputs, + @Nullable CaptureRequest sessionParams) { mSessionType = sessionType; mSessionTemplateId = sessionTemplateId; mOutputs = outputs; diff --git a/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java b/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java index 85d180d379df..9dc6d7bf94b3 100644 --- a/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java +++ b/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java @@ -27,6 +27,10 @@ import java.util.ArrayList; import java.util.List; /** + * Helper class used to describe a single camera + * output configuration that is intended to be configured + * internally by the extension implementation. + * * @hide */ @SystemApi @@ -37,6 +41,21 @@ public class ExtensionOutputConfiguration { private final int mOutputConfigId; private final int mSurfaceGroupId; + /** + * Initialize an extension output configuration instance + * + * @param outputs List of camera {@link CameraOutputSurface outputs}. + * The list may include more than one entry + * only in case of shared camera outputs. + * In all other cases the list will only include + * a single entry. + * @param outputConfigId Unique output configuration id used to identify + * this particular configuration. + * @param physicalCameraId In case of physical camera capture, this field + * must contain a valid physical camera id. + * @param surfaceGroupId In case of surface group, this field must + * contain the surface group id + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) public ExtensionOutputConfiguration(@NonNull List<CameraOutputSurface> outputs, int outputConfigId, @Nullable String physicalCameraId, int surfaceGroupId) { diff --git a/core/java/android/hardware/camera2/extension/RequestProcessor.java b/core/java/android/hardware/camera2/extension/RequestProcessor.java index bf5ea12df358..0ad27c212d67 100644 --- a/core/java/android/hardware/camera2/extension/RequestProcessor.java +++ b/core/java/android/hardware/camera2/extension/RequestProcessor.java @@ -20,7 +20,9 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; @@ -69,6 +71,8 @@ public final class RequestProcessor { * regular request, or the timestamp at the input * image's start of capture for a * reprocess request, in nanoseconds. + * The timestamp matches with and uses the same + * time base as {@link CaptureResult#SENSOR_TIMESTAMP}. * @param frameNumber the frame number for this capture * */ @@ -225,11 +229,12 @@ public final class RequestProcessor { public final static class Request { private final List<Integer> mOutputIds; private final List<Pair<CaptureRequest.Key, Object>> mParameters; - private final int mTemplateId; + private final @CameraDevice.RequestTemplate int mTemplateId; @FlaggedApi(Flags.FLAG_CONCERT_MODE) public Request(@NonNull List<Integer> outputConfigIds, - @NonNull List<Pair<CaptureRequest.Key, Object>> parameters, int templateId) { + @NonNull List<Pair<CaptureRequest.Key, Object>> parameters, + @CameraDevice.RequestTemplate int templateId) { mOutputIds = outputConfigIds; mParameters = parameters; mTemplateId = templateId; @@ -255,7 +260,10 @@ public final class RequestProcessor { } /** - * Gets the template id. + * Gets the request {@link android.hardware.camera2.CameraDevice.RequestTemplate template} + * id. + * + * @see CameraDevice.RequestTemplate */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) Integer getTemplateId() { @@ -310,26 +318,32 @@ public final class RequestProcessor { * Submit a capture request. * @param request Capture request to queued in the Camera2 session * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback Request callback implementation - * @return the id of the capture sequence or -1 in case the processor - * encounters a fatal error or receives an invalid argument. + * @return the id of the capture sequence */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public int submit(@NonNull Request request, @Nullable Executor executor, - @NonNull RequestCallback callback) { + public int submit(@NonNull Request request, @NonNull Executor executor, + @NonNull RequestCallback callback) throws CameraAccessException { ArrayList<Request> requests = new ArrayList<>(1); requests.add(0, request); List<android.hardware.camera2.extension.Request> parcelableRequests = Request.initializeParcelable(mVendorId, requests); + int ret = -1; try { - return mRequestProcessor.submit(parcelableRequests.get(0), + ret = mRequestProcessor.submit(parcelableRequests.get(0), new RequestCallbackImpl(requests, callback, executor)); } catch (RemoteException e) { throw new RuntimeException(e); } + + if (ret == -1) { + throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, + "Failed to submit capture request"); + } + + return ret; } /** @@ -337,24 +351,30 @@ public final class RequestProcessor { * @param requests List of capture requests to be queued in the * Camera2 session * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback Request callback implementation - * @return the id of the capture sequence or -1 in case the processor - * encounters a fatal error or receives an invalid argument. + * @return the id of the capture sequence */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public int submitBurst(@NonNull List<Request> requests, @Nullable Executor executor, - @NonNull RequestCallback callback) { + public int submitBurst(@NonNull List<Request> requests, @NonNull Executor executor, + @NonNull RequestCallback callback) throws CameraAccessException { List<android.hardware.camera2.extension.Request> parcelableRequests = Request.initializeParcelable(mVendorId, requests); + int ret = -1; try { - return mRequestProcessor.submitBurst(parcelableRequests, + ret = mRequestProcessor.submitBurst(parcelableRequests, new RequestCallbackImpl(requests, callback, executor)); } catch (RemoteException e) { throw new RuntimeException(e); } + + if (ret == -1) { + throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, + "Failed to submit burst request"); + } + + return ret; } /** @@ -362,26 +382,32 @@ public final class RequestProcessor { * @param request Repeating capture request to be se in the * Camera2 session * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback Request callback implementation - * @return the id of the capture sequence or -1 in case the processor - * encounters a fatal error or receives an invalid argument. + * @return the id of the capture sequence */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public int setRepeating(@NonNull Request request, @Nullable Executor executor, - @NonNull RequestCallback callback) { + public int setRepeating(@NonNull Request request, @NonNull Executor executor, + @NonNull RequestCallback callback) throws CameraAccessException { ArrayList<Request> requests = new ArrayList<>(1); requests.add(0, request); List<android.hardware.camera2.extension.Request> parcelableRequests = Request.initializeParcelable(mVendorId, requests); + int ret = -1; try { - return mRequestProcessor.setRepeating(parcelableRequests.get(0), + ret = mRequestProcessor.setRepeating(parcelableRequests.get(0), new RequestCallbackImpl(requests, callback, executor)); } catch (RemoteException e) { throw new RuntimeException(e); } + if (ret == -1) { + throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, + "Failed to set the repeating request"); + + } + + return ret; } /** @@ -414,7 +440,7 @@ public final class RequestProcessor { private final Executor mExecutor; public RequestCallbackImpl(@NonNull List<Request> requests, - @NonNull RequestCallback callback, @Nullable Executor executor) { + @NonNull RequestCallback callback, @NonNull Executor executor) { mCallback = callback; mRequests = requests; mExecutor = executor; @@ -425,13 +451,8 @@ public final class RequestProcessor { if (mRequests.get(requestId) != null) { final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute(() -> mCallback.onCaptureStarted( - mRequests.get(requestId), frameNumber, timestamp)); - } else { - mCallback.onCaptureStarted(mRequests.get(requestId), frameNumber, - timestamp); - } + mExecutor.execute(() -> mCallback.onCaptureStarted( + mRequests.get(requestId), frameNumber, timestamp)); } finally { Binder.restoreCallingIdentity(ident); } @@ -448,14 +469,9 @@ public final class RequestProcessor { partialResult.frameNumber); final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute( - () -> mCallback.onCaptureProgressed(mRequests.get(requestId), - result)); - } else { - mCallback.onCaptureProgressed(mRequests.get(requestId), result); - } - + mExecutor.execute( + () -> mCallback.onCaptureProgressed(mRequests.get(requestId), + result)); } finally { Binder.restoreCallingIdentity(ident); } @@ -489,13 +505,9 @@ public final class RequestProcessor { physicalResults); final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute( - () -> mCallback.onCaptureCompleted(mRequests.get(requestId), - result)); - } else { - mCallback.onCaptureCompleted(mRequests.get(requestId), result); - } + mExecutor.execute( + () -> mCallback.onCaptureCompleted(mRequests.get(requestId), + result)); } finally { Binder.restoreCallingIdentity(ident); } @@ -515,12 +527,8 @@ public final class RequestProcessor { captureFailure.errorPhysicalCameraId); final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute(() -> mCallback.onCaptureFailed(mRequests.get(requestId), - failure)); - } else { - mCallback.onCaptureFailed(mRequests.get(requestId), failure); - } + mExecutor.execute(() -> mCallback.onCaptureFailed(mRequests.get(requestId), + failure)); } finally { Binder.restoreCallingIdentity(ident); } @@ -534,14 +542,9 @@ public final class RequestProcessor { if (mRequests.get(requestId) != null) { final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute( - () -> mCallback.onCaptureBufferLost(mRequests.get(requestId), - frameNumber, outputStreamId)); - } else { - mCallback.onCaptureBufferLost(mRequests.get(requestId), frameNumber, - outputStreamId); - } + mExecutor.execute( + () -> mCallback.onCaptureBufferLost(mRequests.get(requestId), + frameNumber, outputStreamId)); } finally { Binder.restoreCallingIdentity(ident); } @@ -554,12 +557,8 @@ public final class RequestProcessor { public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute(() -> mCallback.onCaptureSequenceCompleted(sequenceId, - frameNumber)); - } else { - mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber); - } + mExecutor.execute(() -> mCallback.onCaptureSequenceCompleted(sequenceId, + frameNumber)); } finally { Binder.restoreCallingIdentity(ident); } @@ -569,11 +568,7 @@ public final class RequestProcessor { public void onCaptureSequenceAborted(int sequenceId) { final long ident = Binder.clearCallingIdentity(); try { - if (mExecutor != null) { - mExecutor.execute(() -> mCallback.onCaptureSequenceAborted(sequenceId)); - } else { - mCallback.onCaptureSequenceAborted(sequenceId); - } + mExecutor.execute(() -> mCallback.onCaptureSequenceAborted(sequenceId)); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java index 9c5136bcf903..e7cc5303fc18 100644 --- a/core/java/android/hardware/camera2/extension/SessionProcessor.java +++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java @@ -18,13 +18,15 @@ 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.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.impl.CameraExtensionUtils.HandlerExecutor; import android.hardware.camera2.impl.CameraMetadataNative; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -60,7 +62,7 @@ import java.util.concurrent.Executor; * to the repeating request and single requests but the implementation can * choose to apply some of them only. * - * (5) {@link #startCapture}: It is called when apps want + * (5) {@link #startMultiFrameCapture}: It is called when apps want * to start a multi-frame image capture. {@link CaptureCallback} will be * called to report the status and the output image will be written to the * capture output surface specified in {@link #initSession}. @@ -78,8 +80,11 @@ public abstract class SessionProcessor { private static final String TAG = "SessionProcessor"; private CameraUsageTracker mCameraUsageTracker; + /** + * Initialize a session process instance + */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - protected SessionProcessor() {} + public SessionProcessor() {} void setCameraUsageTracker(CameraUsageTracker tracker) { mCameraUsageTracker = tracker; @@ -87,7 +92,7 @@ public abstract class SessionProcessor { /** * Callback for notifying the status of {@link - * #startCapture} and {@link #startRepeating}. + * #startMultiFrameCapture} and {@link #startRepeating}. * @hide */ @SystemApi @@ -175,7 +180,7 @@ public abstract class SessionProcessor { * @param requestId the capture request id that generated the * capture results. This is the return value of * either {@link #startRepeating} or {@link - * #startCapture}. + * #startMultiFrameCapture}. * @param results The supported capture results. Do note * that if results 'android.jpeg.quality' and * android.jpeg.orientation' are present in the @@ -252,7 +257,14 @@ public abstract class SessionProcessor { * until onCaptureSessionEnd is called. * @param requestProcessor The request processor to be used for * managing capture requests - * @param statsKey Unique key for telemetry + * @param statsKey Unique key that is associated with the + * current Camera2 session and used by the + * framework telemetry. The id can be referenced + * by the extension, in case there is additional + * extension specific telemetry that needs + * to be linked to the regular capture session. + * + * */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) public abstract void onCaptureSessionStart(@NonNull RequestProcessor requestProcessor, @@ -275,13 +287,12 @@ public abstract class SessionProcessor { * repeating request when needed later. * * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback a callback to report the status. * @return the id of the capture sequence. */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public abstract int startRepeating(@Nullable Executor executor, + public abstract int startRepeating(@NonNull Executor executor, @NonNull CaptureCallback callback); /** @@ -309,13 +320,12 @@ public abstract class SessionProcessor { * immediately. * * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback a callback to report the status. * @return the id of the capture sequence. */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public abstract int startCapture(@Nullable Executor executor, + public abstract int startMultiFrameCapture(@NonNull Executor executor, @NonNull CaptureCallback callback); /** @@ -340,15 +350,14 @@ public abstract class SessionProcessor { * @param captureRequest Capture request that includes the respective * triggers. * @param executor the executor which will be used for - * invoking the callbacks or null to use the - * current thread's looper + * invoking the callbacks * @param callback a callback to report the status. * @return the id of the capture sequence. * */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) public abstract int startTrigger(@NonNull CaptureRequest captureRequest, - @Nullable Executor executor, @NonNull CaptureCallback callback); + @NonNull Executor executor, @NonNull CaptureCallback callback); private final class SessionProcessorImpl extends ISessionProcessorImpl.Stub { private long mVendorId = -1; @@ -401,7 +410,8 @@ public abstract class SessionProcessor { @Override public int startRepeating(ICaptureCallback callback) throws RemoteException { - return SessionProcessor.this.startRepeating(/*executor*/ null, + return SessionProcessor.this.startRepeating( + new HandlerExecutor(new Handler(Looper.getMainLooper())), new CaptureCallbackImpl(callback)); } @@ -413,7 +423,8 @@ public abstract class SessionProcessor { @Override public int startCapture(ICaptureCallback callback, boolean isPostviewRequested) throws RemoteException { - return SessionProcessor.this.startCapture(/*executor*/ null, + return SessionProcessor.this.startMultiFrameCapture( + new HandlerExecutor(new Handler(Looper.getMainLooper())), new CaptureCallbackImpl(callback)); } @@ -425,7 +436,8 @@ public abstract class SessionProcessor { @Override public int startTrigger(CaptureRequest captureRequest, ICaptureCallback callback) throws RemoteException { - return SessionProcessor.this.startTrigger(captureRequest, /*executor*/ null, + return SessionProcessor.this.startTrigger(captureRequest, + new HandlerExecutor(new Handler(Looper.getMainLooper())), new CaptureCallbackImpl(callback)); } diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 471f95bb21f3..380962cbc766 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -42,10 +42,12 @@ interface IPermissionManager { void removePermission(String permissionName); - int getPermissionFlags(String packageName, String permissionName, int deviceId, int userId); + int getPermissionFlags(String packageName, String permissionName, String persistentDeviceId, + int userId); void updatePermissionFlags(String packageName, String permissionName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId); + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int userId); void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); @@ -62,10 +64,11 @@ interface IPermissionManager { boolean removeAllowlistedRestrictedPermission(String packageName, String permissionName, int flags, int userId); - void grantRuntimePermission(String packageName, String permissionName, int deviceId, int userId); + void grantRuntimePermission(String packageName, String permissionName, + String persistentDeviceId, int userId); - void revokeRuntimePermission(String packageName, String permissionName, int deviceId, - int userId, String reason); + void revokeRuntimePermission(String packageName, String permissionName, + String persistentDeviceId, int userId, String reason); void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId); @@ -96,7 +99,8 @@ interface IPermissionManager { boolean isRegisteredAttributionSource(in AttributionSourceState source); - int checkPermission(String packageName, String permissionName, int deviceId, int userId); + int checkPermission(String packageName, String permissionName, String persistentDeviceId, + int userId); int checkUidPermission(int uid, String permissionName, int deviceId); } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 4af6e3a9f8d4..d6e8ce701b39 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -28,6 +28,7 @@ import static android.permission.flags.Flags.serverSideAttributionRegistration; import android.Manifest; import android.annotation.CheckResult; import android.annotation.DurationMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -45,6 +46,8 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.PropertyInvalidatedCache; +import android.companion.virtual.VirtualDevice; +import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.AttributionSource; @@ -68,6 +71,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.permission.flags.Flags; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -240,6 +244,8 @@ public final class PermissionManager { private final LegacyPermissionManager mLegacyPermissionManager; + private final VirtualDeviceManager mVirtualDeviceManager; + private final ArrayMap<PackageManager.OnPermissionsChangedListener, IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>(); private PermissionUsageHelper mUsageHelper; @@ -260,6 +266,7 @@ public final class PermissionManager { mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow( "permissionmgr")); mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class); + mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class); } /** @@ -616,15 +623,50 @@ public final class PermissionManager { //@SystemApi public void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user) { + String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId()); + if (persistentDeviceId == null) { + return; + } + + grantRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, user); + } + + /** + * Grant a runtime permission to an application which the application does not already have. The + * permission must have been requested by the application. If the application is not allowed to + * hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or + * permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown. + * + * @param packageName the package to which to grant the permission + * @param permissionName the permission name to grant + * @param persistentDeviceId the device Id to which to grant the permission + * + * @see #revokeRuntimePermission(String, String, String, String) + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) + @SystemApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public void grantRuntimePermission(@NonNull String packageName, + @NonNull String permissionName, @NonNull String persistentDeviceId) { + grantRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, + mContext.getUser()); + } + + private void grantRuntimePermissionInternal(@NonNull String packageName, + @NonNull String permissionName, @NonNull String persistentDeviceId, + @NonNull UserHandle user) { if (DEBUG_TRACE_GRANTS && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) { Log.i(LOG_TAG_TRACE_GRANTS, "App " + mContext.getPackageName() + " is granting " + packageName + " " - + permissionName + " for user " + user.getIdentifier(), new RuntimeException()); + + permissionName + " for user " + user.getIdentifier() + + " for persistent device " + persistentDeviceId, new RuntimeException()); } try { mPermissionManager.grantRuntimePermission(packageName, permissionName, - mContext.getDeviceId(), user.getIdentifier()); + persistentDeviceId, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -642,7 +684,7 @@ public final class PermissionManager { * user {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * * @param packageName the package from which to revoke the permission - * @param permName the permission name to revoke + * @param permissionName the permission name to revoke * @param user the user for which to revoke the permission * @param reason the reason for the revoke, or {@code null} for unspecified * @@ -653,16 +695,56 @@ public final class PermissionManager { @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) //@SystemApi public void revokeRuntimePermission(@NonNull String packageName, - @NonNull String permName, @NonNull UserHandle user, @Nullable String reason) { + @NonNull String permissionName, @NonNull UserHandle user, @Nullable String reason) { + String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId()); + if (persistentDeviceId == null) { + return; + } + + revokeRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, user, + reason); + } + + /** + * Revoke a runtime permission that was previously granted by + * {@link #grantRuntimePermission(String, String, String)}. The permission must + * have been requested by and granted to the application. If the application is not allowed to + * hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or + * permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown. + * + * @param packageName the package from which to revoke the permission + * @param permissionName the permission name to revoke + * @param persistentDeviceId the persistent device id for which to revoke the permission + * @param reason the reason for the revoke, or {@code null} for unspecified + * + * @see #grantRuntimePermission(String, String, String) + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) + @SystemApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public void revokeRuntimePermission(@NonNull String packageName, + @NonNull String permissionName, @NonNull String persistentDeviceId, + @Nullable String reason) { + revokeRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, + mContext.getUser(), reason); + } + + private void revokeRuntimePermissionInternal(@NonNull String packageName, + @NonNull String permissionName, @NonNull String persistentDeviceId, + @NonNull UserHandle user, @Nullable String reason) { if (DEBUG_TRACE_PERMISSION_UPDATES - && shouldTraceGrant(packageName, permName, user.getIdentifier())) { + && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) { Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " " - + permName + " for user " + user.getIdentifier() + " with reason " + + permissionName + " for user " + user.getIdentifier() + + " for persistent device " + + persistentDeviceId + " with reason " + reason, new RuntimeException()); } try { - mPermissionManager.revokeRuntimePermission(packageName, permName, - mContext.getDeviceId(), user.getIdentifier(), reason); + mPermissionManager.revokeRuntimePermission(packageName, permissionName, + persistentDeviceId, user.getIdentifier(), reason); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -687,9 +769,44 @@ public final class PermissionManager { //@SystemApi public int getPermissionFlags(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user) { + String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId()); + if (persistentDeviceId == null) { + return 0; + } + + return getPermissionFlagsInternal(packageName, permissionName, persistentDeviceId, user); + } + + /** + * Gets the state flags associated with a permission. + * + * @param packageName the package name for which to get the flags + * @param permissionName the permission for which to get the flags + * @param persistentDeviceId the persistent device Id for which to get permission flags + * @return the permission flags + * + * @hide + */ + @PackageManager.PermissionFlags + @RequiresPermission(anyOf = { + android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, + android.Manifest.permission.GET_RUNTIME_PERMISSIONS + }) + @SystemApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public int getPermissionFlags(@NonNull String packageName, @NonNull String permissionName, + @NonNull String persistentDeviceId) { + return getPermissionFlagsInternal(packageName, permissionName, persistentDeviceId, + mContext.getUser()); + } + + private int getPermissionFlagsInternal(@NonNull String packageName, + @NonNull String permissionName, @NonNull String persistentDeviceId, + @NonNull UserHandle user) { try { return mPermissionManager.getPermissionFlags(packageName, permissionName, - mContext.getDeviceId(), user.getIdentifier()); + persistentDeviceId, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -715,21 +832,63 @@ public final class PermissionManager { public void updatePermissionFlags(@NonNull String packageName, @NonNull String permissionName, @PackageManager.PermissionFlags int flagMask, @PackageManager.PermissionFlags int flagValues, @NonNull UserHandle user) { + String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId()); + if (persistentDeviceId == null) { + return; + } + + updatePermissionFlagsInternal(packageName, permissionName, flagMask, flagValues, + persistentDeviceId, user); + } + + /** + * Updates the flags associated with a permission by replacing the flags in the specified mask + * with the provided flag values. + * + * @param packageName The package name for which to update the flags + * @param permissionName The permission for which to update the flags + * @param persistentDeviceId The persistent device for which to update the permission flags + * @param flagMask The flags which to replace + * @param flagValues The flags with which to replace + * + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + }) + @SystemApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public void updatePermissionFlags(@NonNull String packageName, @NonNull String permissionName, + @NonNull String persistentDeviceId, + @PackageManager.PermissionFlags int flagMask, + @PackageManager.PermissionFlags int flagValues + ) { + updatePermissionFlagsInternal(packageName, permissionName, flagMask, flagValues, + persistentDeviceId, mContext.getUser()); + } + + private void updatePermissionFlagsInternal(@NonNull String packageName, + @NonNull String permissionName, + @PackageManager.PermissionFlags int flagMask, + @PackageManager.PermissionFlags int flagValues, @NonNull String persistentDeviceId, + @NonNull UserHandle user + ) { if (DEBUG_TRACE_PERMISSION_UPDATES && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) { Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is updating flags for " + packageName + " " + permissionName + " for user " - + user.getIdentifier() + ": " + DebugUtils.flagsToString( - PackageManager.class, "FLAG_PERMISSION_", flagMask) + " := " - + DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_", - flagValues), new RuntimeException()); + + user.getIdentifier() + " for persistentDeviceId " + persistentDeviceId + ": " + + DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_", flagMask) + + " := " + DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_", + flagValues), new RuntimeException()); } try { final boolean checkAdjustPolicyFlagPermission = mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q; mPermissionManager.updatePermissionFlags(packageName, permissionName, flagMask, flagValues, checkAdjustPolicyFlagPermission, - mContext.getDeviceId(), user.getIdentifier()); + persistentDeviceId, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1642,15 +1801,15 @@ public final class PermissionManager { private static final class PackageNamePermissionQuery { final String permName; final String pkgName; - final int deviceId; + final String persistentDeviceId; @UserIdInt final int userId; PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, - int deviceId, @UserIdInt int userId) { + @Nullable String persistentDeviceId, @UserIdInt int userId) { this.permName = permName; this.pkgName = pkgName; - this.deviceId = deviceId; + this.persistentDeviceId = persistentDeviceId; this.userId = userId; } @@ -1658,13 +1817,13 @@ public final class PermissionManager { public String toString() { return TextUtils.formatSimple( "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\", " - + "deviceId=%s, userId=%s\")", - pkgName, permName, deviceId, userId); + + "persistentDeviceId=%s, userId=%s\")", + pkgName, permName, persistentDeviceId, userId); } @Override public int hashCode() { - return Objects.hash(permName, pkgName, deviceId, userId); + return Objects.hash(permName, pkgName, persistentDeviceId, userId); } @Override @@ -1680,17 +1839,17 @@ public final class PermissionManager { } return Objects.equals(permName, other.permName) && Objects.equals(pkgName, other.pkgName) - && deviceId == other.deviceId + && Objects.equals(persistentDeviceId, other.persistentDeviceId) && userId == other.userId; } } /* @hide */ private static int checkPackageNamePermissionUncached( - String permName, String pkgName, int deviceId, @UserIdInt int userId) { + String permName, String pkgName, String persistentDeviceId, @UserIdInt int userId) { try { return ActivityThread.getPermissionManager().checkPermission( - pkgName, permName, deviceId, userId); + pkgName, permName, persistentDeviceId, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1704,7 +1863,7 @@ public final class PermissionManager { @Override public Integer recompute(PackageNamePermissionQuery query) { return checkPackageNamePermissionUncached( - query.permName, query.pkgName, query.deviceId, query.userId); + query.permName, query.pkgName, query.persistentDeviceId, query.userId); } @Override public boolean bypass(PackageNamePermissionQuery query) { @@ -1717,10 +1876,65 @@ public final class PermissionManager { * * @hide */ - public static int checkPackageNamePermission(String permName, String pkgName, int deviceId, - @UserIdInt int userId) { + public int checkPackageNamePermission(String permName, String pkgName, + int deviceId, @UserIdInt int userId) { + String persistentDeviceId = getPersistentDeviceId(deviceId); + return sPackageNamePermissionCache.query( + new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId)); + } + + @Nullable + private String getPersistentDeviceId(int deviceId) { + String persistentDeviceId = null; + + if (deviceId == Context.DEVICE_ID_DEFAULT) { + persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; + } else if (android.companion.virtual.flags.Flags.vdmPublicApis()) { + VirtualDevice virtualDevice = mVirtualDeviceManager.getVirtualDevice(deviceId); + if (virtualDevice == null) { + Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId); + return null; + } + persistentDeviceId = virtualDevice.getPersistentDeviceId(); + if (persistentDeviceId == null) { + Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId); + } + } else { + Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId + + "is not default."); + } + return persistentDeviceId; + } + + /** + * Check whether a package has been granted a permission on a given device. + * <p> + * <strong>Note: </strong>This API returns the underlying permission state + * as-is and is mostly intended for permission managing system apps. To + * perform an access check for a certain app, please use the + * {@link Context#checkPermission} APIs instead. + * + * @param permissionName The name of the permission you are checking for. + * @param packageName The name of the package you are checking against. + * @param persistentDeviceId The persistent device id you are checking against. + * @param userId The user Id associated with context. + * + * @return If the package has the permission on the device, PERMISSION_GRANTED is + * returned. If it does not have the permission on the device, PERMISSION_DENIED + * is returned. + * + * @see PackageManager#PERMISSION_GRANTED + * @see PackageManager#PERMISSION_DENIED + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) + public static int checkPermission(@NonNull String permissionName, @NonNull String packageName, + @NonNull String persistentDeviceId, @UserIdInt int userId) { return sPackageNamePermissionCache.query( - new PackageNamePermissionQuery(permName, pkgName, deviceId, userId)); + new PackageNamePermissionQuery(permissionName, packageName, persistentDeviceId, + userId)); } /** diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl index 11e5ad8789f2..21801c00b701 100644 --- a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl +++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl @@ -38,5 +38,33 @@ interface IPersistentDataBlockService { int getFlashLockState(); boolean hasFrpCredentialHandle(); String getPersistentDataPackageName(); -} + /** + * Returns true if Factory Reset Protection (FRP) is active, meaning the device rebooted and has + * not been able to transition to the FRP inactive state. + */ + boolean isFactoryResetProtectionActive(); + + /** + * Attempts to deactivate Factory Reset Protection (FRP) with the provided secret. If the + * provided secret matches the stored FRP secret, FRP is deactivated and the method returns + * true. Otherwise, FRP state remains unchanged and the method returns false. + */ + boolean deactivateFactoryResetProtection(in byte[] secret); + + /** + * Stores the provided Factory Reset Protection (FRP) secret as the secret to be used for future + * FRP deactivation. The secret must be 32 bytes in length. Setting the all-zeros "default" + * value disables the FRP feature entirely. + * + * It's the responsibility of the caller to ensure that copies of the FRP secret are stored + * securely where they can be recovered and used to deactivate FRP after an untrusted reset. + * This method will store a copy in /data/system and use that to automatically deactivate FRP + * until /data is wiped. + * + * Note that this method does nothing if FRP is currently active. + * + * Returns true if the secret was successfully changed, false otherwise. + */ + boolean setFactoryResetProtectionSecret(in byte[] secret); +} diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java index 6da3206708a7..9b9cc1933b93 100644 --- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java +++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java @@ -16,6 +16,7 @@ package android.service.persistentdata; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -24,30 +25,17 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; +import android.security.Flags; import android.service.oemlock.OemLockManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Interface for reading and writing data blocks to a persistent partition. - * - * Allows writing one block at a time. Namely, each time - * {@link PersistentDataBlockManager#write(byte[])} - * is called, it will overwite the data that was previously written on the block. - * - * Clients can query the size of the currently written block via - * {@link PersistentDataBlockManager#getDataBlockSize()}. - * - * Clients can query the maximum size for a block via - * {@link PersistentDataBlockManager#getMaximumDataBlockSize()} - * - * Clients can read the currently written block by invoking - * {@link PersistentDataBlockManager#read()}. - * - * @hide + * Interface to the persistent data partition. Provides access to information about the state + * of factory reset protection. */ -@SystemApi +@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) @SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE) public class PersistentDataBlockManager { private static final String TAG = PersistentDataBlockManager.class.getSimpleName(); @@ -55,18 +43,32 @@ public class PersistentDataBlockManager { /** * Indicates that the device's bootloader lock state is UNKNOWN. + * + * @hide */ + @SystemApi public static final int FLASH_LOCK_UNKNOWN = -1; /** * Indicates that the device's bootloader is UNLOCKED. + * + * @hide */ + @SystemApi public static final int FLASH_LOCK_UNLOCKED = 0; /** * Indicates that the device's bootloader is LOCKED. + * + * @hide */ + @SystemApi public static final int FLASH_LOCK_LOCKED = 1; - /** @removed mistakenly exposed previously */ + /** + * @removed mistakenly exposed previously + * + * @hide + */ + @SystemApi @IntDef(prefix = { "FLASH_LOCK_" }, value = { FLASH_LOCK_UNKNOWN, FLASH_LOCK_LOCKED, @@ -75,7 +77,9 @@ public class PersistentDataBlockManager { @Retention(RetentionPolicy.SOURCE) public @interface FlashLockState {} - /** @hide */ + /** + * @hide + */ public PersistentDataBlockManager(IPersistentDataBlockService service) { sService = service; } @@ -91,7 +95,10 @@ public class PersistentDataBlockManager { * in which case -1 will be returned. * * @param data the data to write + * + * @hide */ + @SystemApi @SuppressLint("RequiresPermission") public int write(byte[] data) { try { @@ -103,7 +110,10 @@ public class PersistentDataBlockManager { /** * Returns the data block stored on the persistent partition. + * + * @hide */ + @SystemApi @SuppressLint("RequiresPermission") public byte[] read() { try { @@ -117,7 +127,10 @@ public class PersistentDataBlockManager { * Retrieves the size of the block currently written to the persistent partition. * * Return -1 on error. + * + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize() { try { @@ -131,7 +144,10 @@ public class PersistentDataBlockManager { * Retrieves the maximum size allowed for a data block. * * Returns -1 on error. + * + * @hide */ + @SystemApi @SuppressLint("RequiresPermission") public long getMaximumDataBlockSize() { try { @@ -146,7 +162,10 @@ public class PersistentDataBlockManager { * will erase all data written to the persistent data partition. * It will also prevent any further {@link #write} operation until reboot, * in order to prevent a potential race condition. See b/30352311. + * + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE) public void wipe() { try { @@ -160,7 +179,11 @@ public class PersistentDataBlockManager { * Writes a byte enabling or disabling the ability to "OEM unlock" the device. * * @deprecated use {@link OemLockManager#setOemUnlockAllowedByUser(boolean)} instead. + * + * @hide */ + @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE) public void setOemUnlockEnabled(boolean enabled) { try { @@ -174,7 +197,11 @@ public class PersistentDataBlockManager { * Returns whether or not "OEM unlock" is enabled or disabled on this device. * * @deprecated use {@link OemLockManager#isOemUnlockAllowedByUser()} instead. + * + * @hide */ + @SystemApi + @Deprecated @RequiresPermission(anyOf = { android.Manifest.permission.READ_OEM_UNLOCK_STATE, android.Manifest.permission.OEM_UNLOCK_STATE @@ -193,7 +220,10 @@ public class PersistentDataBlockManager { * @return {@link #FLASH_LOCK_LOCKED} if device bootloader is locked, * {@link #FLASH_LOCK_UNLOCKED} if device bootloader is unlocked, or {@link #FLASH_LOCK_UNKNOWN} * if this information cannot be ascertained on this device. + * + * @hide */ + @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.READ_OEM_UNLOCK_STATE, android.Manifest.permission.OEM_UNLOCK_STATE @@ -222,4 +252,73 @@ public class PersistentDataBlockManager { throw e.rethrowFromSystemServer(); } } + + /** + * Returns true if FactoryResetProtection (FRP) is active, meaning the device rebooted and has + * not been able to deactivate FRP because the deactivation secrets were wiped by an untrusted + * factory reset. + */ + @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) + public boolean isFactoryResetProtectionActive() { + try { + return sService.isFactoryResetProtectionActive(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Attempt to deactivate FRP with the provided secret. If the provided secret matches the + * stored FRP secret, FRP is deactivated and the method returns true. Otherwise, FRP state + * remains unchanged and the method returns false. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) + @SystemApi + @RequiresPermission(android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION) + public boolean deactivateFactoryResetProtection(@NonNull byte[] secret) { + try { + return sService.deactivateFactoryResetProtection(secret); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Store the provided FRP secret as the secret to be used for future FRP deactivation. The + * secret must be 32 bytes in length. Setting the all-zeros "default" value disables the FRP + * feature entirely. + * + * To ensure that the device doesn't end up in a bad state if a crash occurs, this method + * should be used in a three-step process: + * + * 1. Generate a new secret and securely store any necessary copies (e.g. by encrypting them + * and calling #write with a new data block that contains both the old encrypted secret + * copies and the new ones). + * 2. Call this method to set the new FRP secret. This will also write the copy used during + * normal boot. + * 3. Delete any old FRP secret copies (e.g. by calling #write with a new data block that + * contains only the new encrypted secret copies). + * + * Note that this method does nothing if FRP is currently active. + * + * This method does not require any permission, but can be called only by the + * PersistentDataBlockService's authorized caller UID. + * + * Returns true if the new secret was successfully written. Returns false if FRP is currently + * active. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) + @SystemApi + @SuppressLint("RequiresPermission") + public boolean setFactoryResetProtectionSecret(@NonNull byte[] secret) { + try { + return sService.setFactoryResetProtectionSecret(secret); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS index ec4410086edf..763c79e20846 100644 --- a/core/java/android/service/voice/OWNERS +++ b/core/java/android/service/voice/OWNERS @@ -4,4 +4,4 @@ include /core/java/android/app/assist/OWNERS # The owner here should not be assist owner liangyuchen@google.com -tuanng@google.com +adudani@google.com diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl index 8fdb2c20e719..055618861391 100644 --- a/core/java/android/service/wearable/IWearableSensingService.aidl +++ b/core/java/android/service/wearable/IWearableSensingService.aidl @@ -31,6 +31,8 @@ oneway interface IWearableSensingService { void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback); void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback); void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback); + void registerDataRequestObserver(int dataType, in RemoteCallback dataRequestCallback, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback); + void unregisterDataRequestObserver(int dataType, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback); void startDetection(in AmbientContextEventRequest request, in String packageName, in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback); void stopDetection(in String packageName); diff --git a/core/java/android/service/wearable/WearableSensingDataRequester.java b/core/java/android/service/wearable/WearableSensingDataRequester.java new file mode 100644 index 000000000000..5a8104f7e0cc --- /dev/null +++ b/core/java/android/service/wearable/WearableSensingDataRequester.java @@ -0,0 +1,87 @@ +/* + * 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.service.wearable; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.wearable.Flags; +import android.app.wearable.WearableSensingDataRequest; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.function.Consumer; + +/** + * An interface to request wearable sensing data. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) +@SystemApi +public interface WearableSensingDataRequester { + + /** An unknown status. */ + int STATUS_UNKNOWN = 0; + + /** The value of the status code that indicates success. */ + int STATUS_SUCCESS = 1; + + /** + * The value of the status code that indicates the request is rejected because the data request + * observer PendingIntent has been cancelled. + */ + int STATUS_OBSERVER_CANCELLED = 2; + + /** + * The value of the status code that indicates the request is rejected because it is larger than + * {@link WearableSensingDataRequest#getMaxRequestSize()}. + */ + int STATUS_TOO_LARGE = 3; + + /** + * The value of the status code that indicates the request is rejected because it exceeds the + * rate limit. See {@link WearableSensingDataRequest#getRateLimit()}. + */ + int STATUS_TOO_FREQUENT = 4; + + /** @hide */ + @IntDef( + prefix = {"STATUS_"}, + value = { + STATUS_UNKNOWN, + STATUS_SUCCESS, + STATUS_OBSERVER_CANCELLED, + STATUS_TOO_LARGE, + STATUS_TOO_FREQUENT + }) + @Retention(RetentionPolicy.SOURCE) + @interface StatusCode {} + + /** + * Sends a data request. See {@link WearableSensingService#onDataRequestObserverRegistered(int, + * String, WearableSensingDataRequester, Consumer)} for size and rate restrictions on data + * requests. + * + * @param dataRequest The data request to send. + * @param statusConsumer A consumer that handles the status code for the data request. + */ + void requestData( + @NonNull WearableSensingDataRequest dataRequest, + @NonNull @StatusCode Consumer<Integer> statusConsumer); +} diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java index d21115b5d622..d25cff7d35c7 100644 --- a/core/java/android/service/wearable/WearableSensingService.java +++ b/core/java/android/service/wearable/WearableSensingService.java @@ -25,6 +25,7 @@ import android.app.Service; import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; import android.app.wearable.Flags; +import android.app.wearable.WearableSensingDataRequest; import android.app.wearable.WearableSensingManager; import android.content.Intent; import android.os.Bundle; @@ -36,6 +37,7 @@ import android.os.SharedMemory; import android.service.ambientcontext.AmbientContextDetectionResult; import android.service.ambientcontext.AmbientContextDetectionServiceStatus; import android.util.Slog; +import android.util.SparseArray; import java.util.Arrays; import java.util.HashSet; @@ -90,6 +92,9 @@ public abstract class WearableSensingService extends Service { public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService"; + private final SparseArray<WearableSensingDataRequester> mDataRequestObserverIdToRequesterMap = + new SparseArray<>(); + @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { @@ -128,6 +133,55 @@ public abstract class WearableSensingService extends Service { /** {@inheritDoc} */ @Override + public void registerDataRequestObserver( + int dataType, + RemoteCallback dataRequestCallback, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + Objects.requireNonNull(dataRequestCallback); + Objects.requireNonNull(statusCallback); + WearableSensingDataRequester dataRequester; + synchronized (mDataRequestObserverIdToRequesterMap) { + dataRequester = + mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId); + if (dataRequester == null) { + dataRequester = createDataRequester(dataRequestCallback); + mDataRequestObserverIdToRequesterMap.put( + dataRequestObserverId, dataRequester); + } + } + Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback); + WearableSensingService.this.onDataRequestObserverRegistered( + dataType, packageName, dataRequester, statusConsumer); + } + + @Override + public void unregisterDataRequestObserver( + int dataType, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + WearableSensingDataRequester dataRequester; + synchronized (mDataRequestObserverIdToRequesterMap) { + dataRequester = + mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId); + if (dataRequester == null) { + Slog.w( + TAG, + "dataRequestObserverId not found, cannot unregister data" + + " request observer."); + return; + } + mDataRequestObserverIdToRequesterMap.remove(dataRequestObserverId); + } + Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback); + WearableSensingService.this.onDataRequestObserverUnregistered( + dataType, packageName, dataRequester, statusConsumer); + } + + /** {@inheritDoc} */ + @Override public void startDetection( @NonNull AmbientContextEventRequest request, String packageName, @@ -231,19 +285,19 @@ public abstract class WearableSensingService extends Service { @NonNull Consumer<Integer> statusConsumer); /** - * Called when configurations and read-only data in a {@link PersistableBundle} - * can be used by the WearableSensingService and sends the result to the {@link Consumer} - * right after the call. It is dependent on the application to define the type of data to - * provide. This is used by applications that will also provide an implementation of an isolated - * WearableSensingService. If the data was provided successfully - * {@link WearableSensingManager#STATUS_SUCCESS} will be provided. + * Called when configurations and read-only data in a {@link PersistableBundle} can be used by + * the WearableSensingService and sends the result to the {@link Consumer} right after the call. + * It is dependent on the application to define the type of data to provide. This is used by + * applications that will also provide an implementation of an isolated WearableSensingService. + * If the data was provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be + * provided. * * @param data Application configuration data to provide to the {@link WearableSensingService}. - * PersistableBundle does not allow any remotable objects or other contents - * that can be used to communicate with other processes. - * @param sharedMemory The unrestricted data blob to - * provide to the {@link WearableSensingService}. Use this to provide the - * sensing models data or other such data to the trusted process. + * PersistableBundle does not allow any remotable objects or other contents that can be used + * to communicate with other processes. + * @param sharedMemory The unrestricted data blob to provide to the {@link + * WearableSensingService}. Use this to provide the sensing models data or other such data + * to the trusted process. * @param statusConsumer the consumer for the service status. */ @BinderThread @@ -253,6 +307,68 @@ public abstract class WearableSensingService extends Service { @NonNull Consumer<Integer> statusConsumer); /** + * Called when a data request observer is registered. Each request must not be larger than + * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link + * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link + * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too + * frequent will be dropped by the system. See {@link + * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details + * about the status code returned for each request. + * + * <p>The implementing class should override this method. After the data requester is received, + * it should send a {@link WearableSensingManager#STATUS_SUCCESS} status code to the {@code + * statusConsumer} unless it encounters an error condition described by a status code listed in + * {@link WearableSensingManager}, such as {@link + * WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE}, in which case it should return the + * corresponding status code. + * + * @param dataType The data type the observer is registered for. Values are defined by the + * application that implements this class. + * @param packageName The package name of the app that will receive the requests. + * @param dataRequester A handle to the observer registered. It can be used to request data of + * the specified data type. + * @param statusConsumer the consumer for the status of the data request observer registration. + * This is different from the status for each data request. + */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + @BinderThread + public void onDataRequestObserverRegistered( + int dataType, + @NonNull String packageName, + @NonNull WearableSensingDataRequester dataRequester, + @NonNull Consumer<Integer> statusConsumer) { + statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION); + } + + /** + * Called when a data request observer is unregistered. + * + * <p>The implementing class should override this method. It should send a {@link + * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it + * encounters an error condition described by a status code listed in {@link + * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE}, + * in which case it should return the corresponding status code. + * + * @param dataType The data type the observer is for. + * @param packageName The package name of the app that will receive the requests sent to the + * dataRequester. + * @param dataRequester A handle to the observer to be unregistered. It is the exact same + * instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String, + * WearableSensingDataRequester, Consumer)} invocation. + * @param statusConsumer the consumer for the status of the data request observer + * unregistration. This is different from the status for each data request. + */ + @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) + @BinderThread + public void onDataRequestObserverUnregistered( + int dataType, + @NonNull String packageName, + @NonNull WearableSensingDataRequester dataRequester, + @NonNull Consumer<Integer> statusConsumer) { + statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION); + } + + /** * Called when a client app requests starting detection of the events in the request. The * implementation should keep track of whether the user has explicitly consented to detecting * the events using on-going ambient sensor (e.g. microphone), and agreed to share the @@ -309,6 +425,25 @@ public abstract class WearableSensingService extends Service { return intArray; } + private static WearableSensingDataRequester createDataRequester( + RemoteCallback dataRequestCallback) { + return (request, requestStatusConsumer) -> { + Bundle bundle = new Bundle(); + bundle.putParcelable(WearableSensingDataRequest.REQUEST_BUNDLE_KEY, request); + RemoteCallback requestStatusCallback = + new RemoteCallback( + requestStatusBundle -> { + requestStatusConsumer.accept( + requestStatusBundle.getInt( + WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY)); + }); + bundle.putParcelable( + WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY, + requestStatusCallback); + dataRequestCallback.sendResult(bundle); + }; + } + @NonNull private static Consumer<Integer> createWearableStatusConsumer(RemoteCallback statusCallback) { return response -> { diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index 27c509a8605f..52f48553c414 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -183,6 +183,7 @@ public interface AttachedSurfaceControl { * {@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. */ diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 9b21b765a874..270bf8b17aac 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -32,6 +32,7 @@ import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -173,6 +174,8 @@ public final class PointerIcon implements Parcelable { private Bitmap mBitmapFrames[]; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mDurationPerFrame; + @SuppressWarnings("unused") + private boolean mDrawNativeDropShadow; private PointerIcon(int type) { mType = type; @@ -231,9 +234,15 @@ public final class PointerIcon implements Parcelable { typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT); } - final int defStyle = useLargeIcons - ? com.android.internal.R.style.LargePointer - : com.android.internal.R.style.Pointer; + final int defStyle; + // TODO(b/305193969): Use scaled vectors when large icons are requested. + if (useLargeIcons) { + defStyle = com.android.internal.R.style.LargePointer; + } else if (android.view.flags.Flags.enableVectorCursors()) { + defStyle = com.android.internal.R.style.VectorPointer; + } else { + defStyle = com.android.internal.R.style.Pointer; + } TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Pointer, 0, defStyle); @@ -333,6 +342,7 @@ public final class PointerIcon implements Parcelable { Bitmap.CREATOR.createFromParcel(in), in.readFloat(), in.readFloat()); + icon.mDrawNativeDropShadow = in.readBoolean(); return icon; } @@ -362,6 +372,7 @@ public final class PointerIcon implements Parcelable { mBitmap.writeToParcel(out, flags); out.writeFloat(mHotSpotX); out.writeFloat(mHotSpotY); + out.writeBoolean(mDrawNativeDropShadow); } @Override @@ -415,6 +426,16 @@ public final class PointerIcon implements Parcelable { return scaled; } + private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources, + VectorDrawable vectorDrawable) { + Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), + vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + vectorDrawable.draw(canvas); + return new BitmapDrawable(resources, bitmap); + } + private void loadResource(Context context, Resources resources, @XmlRes int resourceId) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; @@ -476,6 +497,10 @@ public final class PointerIcon implements Parcelable { } } } + if (drawable instanceof VectorDrawable) { + mDrawNativeDropShadow = true; + drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable); + } if (!(drawable instanceof BitmapDrawable)) { throw new IllegalArgumentException("<pointer-icon> bitmap attribute must " + "refer to a bitmap drawable."); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5c5817feb23b..a9f189700789 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5543,7 +5543,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // The preferred frame rate of the view that is mainly used for // touch boosting, view velocity handling, and TextureView. - private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT; + private float mPreferredFrameRate = Float.NaN; private int mInfrequentUpdateCount = 0; private long mLastUpdateTimeMillis = 0; @@ -33186,25 +33186,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float sizePercentage = getSizePercentage(); int frameRateCateogry = calculateFrameRateCategory(sizePercentage); if (viewRootImpl != null && sizePercentage > 0) { - if (mPreferredFrameRate < 0) { - if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { - frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { - frameRateCateogry = FRAME_RATE_CATEGORY_LOW; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { - frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { - frameRateCateogry = FRAME_RATE_CATEGORY_HIGH; + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + viewRootImpl.recordViewPercentage(sizePercentage); + } + if (!Float.isNaN(mPreferredFrameRate)) { + if (mPreferredFrameRate < 0) { + if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { + frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { + frameRateCateogry = FRAME_RATE_CATEGORY_LOW; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { + frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { + frameRateCateogry = FRAME_RATE_CATEGORY_HIGH; + } + } else { + viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); + return; } - } else { - viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); } viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); mLastFrameRateCategory = frameRateCateogry; - - if (sToolkitMetricsForFrameRateDecisionFlagValue) { - viewRootImpl.recordViewPercentage(sizePercentage); - } } } diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index 6a4408bdd391..3e7a9cb34878 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -17,3 +17,11 @@ flag { bug: "316170253" is_fixed_read_only: true } + +flag { + name: "enable_vector_cursors" + namespace: "systemui" + description: "Feature flag to enable vector drawables in addition to bitmaps for PointerIcon." + bug: "305193969" + is_fixed_read_only: true +} diff --git a/core/java/android/view/flags/window_insets.aconfig b/core/java/android/view/flags/window_insets.aconfig new file mode 100644 index 000000000000..201b7ad62f14 --- /dev/null +++ b/core/java/android/view/flags/window_insets.aconfig @@ -0,0 +1,9 @@ +package: "android.view.flags" + +flag { + name: "customizable_window_headers" + namespace: "lse_desktop_experience" + description: "Flag to control the caption bar appearance and to fit app content in its empty space" + bug: "316387515" + is_fixed_read_only: true +} diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java index b0f35784fbbd..a051c1b8917e 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java @@ -89,4 +89,7 @@ public interface ParsedActivity extends ParsedMainComponent { */ @Nullable String getRequiredDisplayCategory(); + + /** Gets the permissions necessary for launching the activity when using content URIs. */ + int getRequireContentUriPermissionFromCaller(); } diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java index 2f977eea127d..12187931d2c9 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java @@ -36,9 +36,9 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.internal.pm.pkg.parsing.ParsingUtils; import java.util.Collections; import java.util.Locale; @@ -97,6 +97,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse @Nullable private String mRequiredDisplayCategory; + private int mRequireContentUriPermissionFromCaller; + public ParsedActivityImpl(ParsedActivityImpl other) { super(other); this.theme = other.theme; @@ -124,6 +126,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse this.windowLayout = other.windowLayout; this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts; this.mRequiredDisplayCategory = other.mRequiredDisplayCategory; + this.mRequireContentUriPermissionFromCaller = other.mRequireContentUriPermissionFromCaller; } /** @@ -192,6 +195,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse alias.setDirectBootAware(target.isDirectBootAware()); alias.setProcessName(target.getProcessName()); alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory()); + alias.setRequireContentUriPermissionFromCaller( + target.getRequireContentUriPermissionFromCaller()); return alias; // Not all attributes from the target ParsedActivity are copied to the alias. @@ -320,6 +325,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse } sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags); dest.writeString8(this.mRequiredDisplayCategory); + dest.writeInt(this.mRequireContentUriPermissionFromCaller); } public ParsedActivityImpl() { @@ -355,6 +361,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse } this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in); this.mRequiredDisplayCategory = in.readString8(); + this.mRequireContentUriPermissionFromCaller = in.readInt(); } @NonNull @@ -412,7 +419,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse int rotationAnimation, int colorMode, @Nullable ActivityInfo.WindowLayout windowLayout, - @Nullable String requiredDisplayCategory) { + @Nullable String requiredDisplayCategory, + int requireContentUriPermissionFromCaller) { this.theme = theme; this.uiOptions = uiOptions; this.targetActivity = targetActivity; @@ -438,6 +446,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse this.colorMode = colorMode; this.windowLayout = windowLayout; this.mRequiredDisplayCategory = requiredDisplayCategory; + this.mRequireContentUriPermissionFromCaller = requireContentUriPermissionFromCaller; // onConstructed(); // You can define this method to get a callback } @@ -563,6 +572,11 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse } @DataClass.Generated.Member + public int getRequireContentUriPermissionFromCaller() { + return mRequireContentUriPermissionFromCaller; + } + + @DataClass.Generated.Member public @NonNull ParsedActivityImpl setTheme( int value) { theme = value; return this; @@ -694,11 +708,17 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse return this; } + @DataClass.Generated.Member + public @NonNull ParsedActivityImpl setRequireContentUriPermissionFromCaller( int value) { + mRequireContentUriPermissionFromCaller = value; + return this; + } + @DataClass.Generated( - time = 1701338377709L, + time = 1706180262165L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java", - inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\nprivate int mRequireContentUriPermissionFromCaller\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java index c3f7dab3c46a..9f71d88c24bc 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java @@ -241,6 +241,10 @@ public class ParsedActivityUtils { activity.setRequiredDisplayCategory(requiredDisplayCategory); + activity.setRequireContentUriPermissionFromCaller(sa.getInt( + R.styleable.AndroidManifestActivity_requireContentUriPermissionFromCaller, + ActivityInfo.CONTENT_URI_PERMISSION_NONE)); + return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver, false /*isAlias*/, visibleToEphemeral, input, R.styleable.AndroidManifestActivity_parentActivityName, diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java index 3ab6c4744fb9..a7260bb7c14b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -289,6 +289,7 @@ public class RemoteComposeBuffer { try { byte[] bytes = readAllBytes(fd); buffer.reset(); + buffer.mBuffer.resize(bytes.length); System.arraycopy(bytes, 0, buffer.mBuffer.mBuffer, 0, bytes.length); buffer.mBuffer.mSize = bytes.length; } catch (Exception e) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java index 3e701c17291b..4518d94498d6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java @@ -37,7 +37,7 @@ public class WireBuffer { this(BUFFER_SIZE); } - private void resize(int need) { + public void resize(int need) { if (mSize + need >= mMaxSize) { mMaxSize = Math.max(mMaxSize * 2, mSize + need); mBuffer = Arrays.copyOf(mBuffer, mMaxSize); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 3fc16833fc70..240028c191bc 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -98,6 +98,7 @@ cc_library_shared_for_libandroid_runtime { "libminikin", "libz", "server_configurable_flags", + "android.media.audiopolicy-aconfig-cc", ], static_libs: [ diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 969e47b6a803..070d07c22fbc 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -22,6 +22,7 @@ #include <android/media/INativeSpatializerCallback.h> #include <android/media/ISpatializer.h> #include <android/media/audio/common/AudioConfigBase.h> +#include <android_media_audiopolicy.h> #include <android_os_Parcel.h> #include <audiomanager/AudioManager.h> #include <jni.h> @@ -55,6 +56,8 @@ // ---------------------------------------------------------------------------- +namespace audio_flags = android::media::audiopolicy; + using namespace android; using media::audio::common::AudioConfigBase; @@ -145,6 +148,7 @@ static struct { } gAudioPatchFields; static jclass gAudioMixClass; +static jmethodID gAudioMixCstor; static struct { jfieldID mRule; jfieldID mFormat; @@ -165,7 +169,15 @@ static struct { // other fields unused by JNI } gAudioFormatFields; +static jclass gAudioAttributesClass; +static jmethodID gAudioAttributesCstor; +static struct { + jfieldID mSource; + jfieldID mUsage; +} gAudioAttributesFields; + static jclass gAudioMixingRuleClass; +static jmethodID gAudioMixingRuleCstor; static struct { jfieldID mCriteria; jfieldID mAllowPrivilegedPlaybackCapture; @@ -174,6 +186,8 @@ static struct { } gAudioMixingRuleFields; static jclass gAudioMixMatchCriterionClass; +static jmethodID gAudioMixMatchCriterionAttrCstor; +static jmethodID gAudioMixMatchCriterionIntPropCstor; static struct { jfieldID mAttr; jfieldID mIntProp; @@ -2087,6 +2101,39 @@ jobject nativeAudioConfigBaseToJavaAudioFormat(JNIEnv *env, const audio_config_b channelMask, channelIndexMask); } +jint nativeAudioConfigToJavaAudioFormat(JNIEnv *env, const audio_config_t *nConfigBase, + jobject *jAudioFormat, bool isInput) { + if (!audio_flags::audio_mix_test_api()) { + return AUDIO_JAVA_INVALID_OPERATION; + } + + if (nConfigBase == nullptr) { + return AUDIO_JAVA_BAD_VALUE; + } + int propertyMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING | AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE; + int channelMask = 0; + int channelIndexMask = 0; + switch (audio_channel_mask_get_representation(nConfigBase->channel_mask)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + channelMask = isInput ? inChannelMaskFromNative(nConfigBase->channel_mask) + : outChannelMaskFromNative(nConfigBase->channel_mask); + propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK; + break; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + channelIndexMask = audio_channel_mask_get_bits(nConfigBase->channel_mask); + propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK; + break; + default: + // This must not happen + break; + } + + *jAudioFormat = env->NewObject(gAudioFormatClass, gAudioFormatCstor, propertyMask, + audioFormatFromNative(nConfigBase->format), + nConfigBase->sample_rate, channelMask, channelIndexMask); + return AUDIO_JAVA_SUCCESS; +} + jint convertAudioMixerAttributesToNative(JNIEnv *env, const jobject jAudioMixerAttributes, audio_mixer_attributes_t *nMixerAttributes) { ScopedLocalRef<jobject> jFormat(env, @@ -2179,6 +2226,88 @@ static jint convertAudioMixingRuleToNative(JNIEnv *env, const jobject audioMixin return AUDIO_JAVA_SUCCESS; } +static jint nativeAudioMixToJavaAudioMixingRule(JNIEnv *env, const AudioMix &nAudioMix, + jobject *jAudioMixingRule) { + if (!audio_flags::audio_mix_test_api()) { + return AUDIO_JAVA_INVALID_OPERATION; + } + + jobject jAudioMixMatchCriterionList = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + for (const auto &criteria : nAudioMix.mCriteria) { + jobject jAudioAttributes = NULL; + jobject jMixMatchCriterion = NULL; + jobject jValueInteger = NULL; + switch (criteria.mRule) { + case RULE_MATCH_UID: + jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUid); + jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass, + gAudioMixMatchCriterionIntPropCstor, + jValueInteger, criteria.mRule); + break; + case RULE_MATCH_USERID: + jValueInteger = + env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUserId); + jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass, + gAudioMixMatchCriterionIntPropCstor, + jValueInteger, criteria.mRule); + break; + case RULE_MATCH_AUDIO_SESSION_ID: + jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor, + criteria.mValue.mAudioSessionId); + jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass, + gAudioMixMatchCriterionIntPropCstor, + jValueInteger, criteria.mRule); + break; + case RULE_MATCH_ATTRIBUTE_USAGE: + jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor); + env->SetIntField(jAudioAttributes, gAudioAttributesFields.mUsage, + criteria.mValue.mUsage); + jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass, + gAudioMixMatchCriterionAttrCstor, + jMixMatchCriterion, criteria.mRule); + break; + case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: + jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor); + env->SetIntField(jAudioAttributes, gAudioAttributesFields.mSource, + criteria.mValue.mSource); + jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass, + gAudioMixMatchCriterionAttrCstor, + jMixMatchCriterion, criteria.mRule); + break; + } + env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add, + jMixMatchCriterion); + } + + *jAudioMixingRule = env->NewObject(gAudioMixingRuleClass, gAudioMixingRuleCstor, + nAudioMix.mMixType, jAudioMixMatchCriterionList, + nAudioMix.mAllowPrivilegedMediaPlaybackCapture, + nAudioMix.mVoiceCommunicationCaptureAllowed); + return AUDIO_JAVA_SUCCESS; +} + +static jint convertAudioMixFromNative(JNIEnv *env, jobject *jAudioMix, const AudioMix &nAudioMix) { + if (!audio_flags::audio_mix_test_api()) { + return AUDIO_JAVA_INVALID_OPERATION; + } + jobject jAudioMixingRule = NULL; + int status = nativeAudioMixToJavaAudioMixingRule(env, nAudioMix, &jAudioMixingRule); + if (status != AUDIO_JAVA_SUCCESS) { + return status; + } + jobject jAudioFormat = NULL; + status = nativeAudioConfigToJavaAudioFormat(env, &nAudioMix.mFormat, &jAudioFormat, false); + if (status != AUDIO_JAVA_SUCCESS) { + return status; + } + + jstring deviceAddress = env->NewStringUTF(nAudioMix.mDeviceAddress.c_str()); + *jAudioMix = env->NewObject(gAudioMixClass, gAudioMixCstor, jAudioMixingRule, jAudioFormat, + nAudioMix.mRouteFlags, nAudioMix.mCbFlags, nAudioMix.mDeviceType, + deviceAddress); + return AUDIO_JAVA_SUCCESS; +} + static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, const jobject jAudioMix) { nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType); nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags); @@ -2252,6 +2381,34 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, return nativeToJavaStatus(status); } +static jint android_media_AudioSystem_getRegisteredPolicyMixes(JNIEnv *env, jobject clazz, + jobject jMixes) { + if (!audio_flags::audio_mix_test_api()) { + return AUDIO_JAVA_INVALID_OPERATION; + } + + status_t status; + std::vector<AudioMix> mixes; + ALOGV("AudioSystem::getRegisteredPolicyMixes"); + status = AudioSystem::getRegisteredPolicyMixes(mixes); + ALOGV("AudioSystem::getRegisteredPolicyMixes() returned %zu mixes. Status=%d", mixes.size(), + status); + if (status != NO_ERROR) { + return nativeToJavaStatus(status); + } + + for (const auto &mix : mixes) { + jobject jAudioMix = NULL; + int conversionStatus = convertAudioMixFromNative(env, &jAudioMix, mix); + if (conversionStatus != AUDIO_JAVA_SUCCESS) { + return conversionStatus; + } + env->CallBooleanMethod(jMixes, gListMethods.add, jAudioMix); + } + + return AUDIO_JAVA_SUCCESS; +} + static jint android_media_AudioSystem_updatePolicyMixes(JNIEnv *env, jobject clazz, jobjectArray mixes, jobjectArray updatedMixingRules) { @@ -3251,6 +3408,8 @@ static const JNINativeMethod gMethods[] = MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", android_media_AudioSystem_registerPolicyMixes), + MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I", + android_media_AudioSystem_getRegisteredPolicyMixes), MAKE_JNI_NATIVE_METHOD("updatePolicyMixes", "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/" "AudioMixingRule;)I", @@ -3499,6 +3658,11 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix"); gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass); + if (audio_flags::audio_mix_test_api()) { + gAudioMixCstor = GetMethodIDOrDie(env, audioMixClass, "<init>", + "(Landroid/media/audiopolicy/AudioMixingRule;Landroid/" + "media/AudioFormat;IIILjava/lang/String;)V"); + } gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule", "Landroid/media/audiopolicy/AudioMixingRule;"); gAudioMixFields.mFormat = GetFieldIDOrDie(env, audioMixClass, "mFormat", @@ -3521,6 +3685,10 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass audioMixingRuleClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule"); gAudioMixingRuleClass = MakeGlobalRefOrDie(env, audioMixingRuleClass); + if (audio_flags::audio_mix_test_api()) { + gAudioMixingRuleCstor = GetMethodIDOrDie(env, audioMixingRuleClass, "<init>", + "(ILjava/util/Collection;ZZ)V"); + } gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria", "Ljava/util/ArrayList;"); gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture = @@ -3529,9 +3697,24 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed = GetFieldIDOrDie(env, audioMixingRuleClass, "mVoiceCommunicationCaptureAllowed", "Z"); + if (audio_flags::audio_mix_test_api()) { + jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes"); + gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass); + gAudioAttributesCstor = GetMethodIDOrDie(env, gAudioAttributesClass, "<init>", "()V"); + gAudioAttributesFields.mSource = GetFieldIDOrDie(env, gAudioAttributesClass, "mUsage", "I"); + gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, gAudioAttributesClass, "mSource", "I"); + } + jclass audioMixMatchCriterionClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion"); gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass); + if (audio_flags::audio_mix_test_api()) { + gAudioMixMatchCriterionAttrCstor = + GetMethodIDOrDie(env, gAudioMixMatchCriterionClass, "<init>", + "(Landroid/media/AudioAttributes;I)V"); + gAudioMixMatchCriterionIntPropCstor = GetMethodIDOrDie(env, gAudioMixMatchCriterionClass, + "<init>", "(Ljava/lang/Integer;I)V"); + } gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr", "Landroid/media/AudioAttributes;"); gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp", diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp index c6a3b52d9397..86b0009227b3 100644 --- a/core/jni/android_view_PointerIcon.cpp +++ b/core/jni/android_view_PointerIcon.cpp @@ -37,6 +37,7 @@ static struct { jfieldID mHotSpotY; jfieldID mBitmapFrames; jfieldID mDurationPerFrame; + jfieldID mDrawNativeDropShadow; } gPointerIconClassInfo; @@ -51,6 +52,8 @@ PointerIcon android_view_PointerIcon_toNative(JNIEnv* env, jobject pointerIconOb env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType)); icon.hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX); icon.hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY); + icon.drawNativeDropShadow = + env->GetBooleanField(pointerIconObj, gPointerIconClassInfo.mDrawNativeDropShadow); ScopedLocalRef<jobject> bitmapObj( env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap)); @@ -95,6 +98,9 @@ int register_android_view_PointerIcon(JNIEnv* env) { gPointerIconClassInfo.mBitmapFrames = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz, "mBitmapFrames", "[Landroid/graphics/Bitmap;"); + gPointerIconClassInfo.mDrawNativeDropShadow = + GetFieldIDOrDie(env, gPointerIconClassInfo.clazz, "mDrawNativeDropShadow", "Z"); + gPointerIconClassInfo.mDurationPerFrame = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz, "mDurationPerFrame", "I"); diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h index ee446fb92a4f..1b6a3977bf61 100644 --- a/core/jni/android_view_PointerIcon.h +++ b/core/jni/android_view_PointerIcon.h @@ -39,6 +39,7 @@ struct PointerIcon { float hotSpotY; std::vector<graphics::Bitmap> bitmapFrames; int32_t durationPerFrame; + bool drawNativeDropShadow; inline bool isNullIcon() { return style == PointerIconStyle::TYPE_NULL; } @@ -49,6 +50,7 @@ struct PointerIcon { hotSpotY = 0; bitmapFrames.clear(); durationPerFrame = 0; + drawNativeDropShadow = false; } }; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 58166bf513b9..0938ce17a4c0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -2416,6 +2416,11 @@ pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server, const std::vector<int>& fds_to_ignore, bool is_priority_fork, bool purge) { + ATRACE_CALL(); + if (is_priority_fork) { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX); + } + SetSignalHandlers(); // Curry a failure function. @@ -2501,6 +2506,10 @@ pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server, // We blocked SIGCHLD prior to a fork, we unblock it here. UnblockSignal(SIGCHLD, fail_fn); + if (is_priority_fork && pid != 0) { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT); + } + return pid; } @@ -2570,6 +2579,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) { + ATRACE_CALL(); std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()), fds_to_ignore(fds_to_close); @@ -2645,6 +2655,7 @@ static jint com_android_internal_os_Zygote_nativeForkApp(JNIEnv* env, jintArray managed_session_socket_fds, jboolean args_known, jboolean is_priority_fork) { + ATRACE_CALL(); std::vector<int> session_socket_fds = ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds) .value_or(std::vector<int>()); @@ -2660,6 +2671,7 @@ int zygote::forkApp(JNIEnv* env, bool args_known, bool is_priority_fork, bool purge) { + ATRACE_CALL(); std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()), fds_to_ignore(fds_to_close); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5c764e2f0e7d..a425bb0e7461 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2772,6 +2772,12 @@ <permission android:name="android.permission.OEM_UNLOCK_STATE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows configuration of factory reset protection + @FlaggedApi("android.security.frp_enforcement") + @hide <p>Not for use by third-party applications. --> + <permission android:name="android.permission.CONFIGURE_FACTORY_RESET_PROTECTION" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows querying state of PersistentDataBlock <p>Not for use by third-party applications. --> <permission android:name="android.permission.ACCESS_PDB_STATE" @@ -7562,6 +7568,11 @@ <permission android:name="android.permission.RESET_APP_ERRORS" android:protectionLevel="signature" /> + <!-- @hide Allows ThemeOverlayController to delay launch of Home / SetupWizard on boot, ensuring + Theme Palettes and Colors are ready --> + <permission android:name="android.permission.SET_THEME_OVERLAY_CONTROLLER_READY" + android:protectionLevel="signature|setup" /> + <!-- @hide Allows an application to create/destroy input consumer. --> <permission android:name="android.permission.INPUT_CONSUMER" android:protectionLevel="signature" /> diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml new file mode 100644 index 000000000000..74dd6a0b2a23 --- /dev/null +++ b/core/res/res/drawable/pointer_alias_vector.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#00FFFFFF" + android:pathData="M14.494 12.779a5.2 5.2 0 0 1-1.771 1.414 5.2 5.2 0 0 1-1.68.489l.81 1.658a1.968 1.968 0 0 0 3.536-1.728zM12.03 8.291l-.81-1.658a1.968 1.968 0 0 0-3.536 1.728l.896 1.833a5.2 5.2 0 0 1 1.77-1.414 5.2 5.2 0 0 1 1.68-.489" /> + <path + android:fillColor="#FFFFFF" + android:pathData="m18.323 13.178-.975-1.995a5.2 5.2 0 0 0-1.704-1.978 5.2 5.2 0 0 0-.517-2.01L14.152 5.2a5.232 5.232 0 0 0-9.401 4.594l.975 1.995a5.2 5.2 0 0 0 1.704 1.978 5.2 5.2 0 0 0 .517 2.01l.975 1.995a5.233 5.233 0 0 0 9.401-4.594m-2.843 6.1a4.23 4.23 0 0 1-5.66-1.944l-.975-1.995a4.2 4.2 0 0 1-.431-1.838l-.001-.276-.234-.146a4.2 4.2 0 0 1-1.555-1.729L5.65 9.355a4.232 4.232 0 1 1 7.604-3.716l.975 1.995c.29.594.428 1.22.431 1.838l.001.276.234.146c.648.405 1.194.99 1.555 1.728l.975 1.995a4.234 4.234 0 0 1-1.945 5.661" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" /> + <path + android:fillColor="#000000" + android:pathData="M16.449 11.622a4.2 4.2 0 0 0-1.555-1.728l-.234-.146-.001-.276a4.2 4.2 0 0 0-.431-1.838l-.975-1.995a4.232 4.232 0 1 0-7.604 3.716l.975 1.995a4.2 4.2 0 0 0 1.555 1.729l.234.146.001.276c.002.617.141 1.244.431 1.838l.975 1.995a4.232 4.232 0 1 0 7.604-3.716zm-7.814.34-.459-.534a3 3 0 0 1-.416-.633L6.785 8.8a2.967 2.967 0 1 1 5.332-2.606l.975 1.995q.098.202.164.409l.214.672-.704-.02a4.2 4.2 0 0 0-1.977.429 4.2 4.2 0 0 0-1.801 1.675zm1.689-.33a2.97 2.97 0 0 1 1.975-1.1l1.205-.142-.755.95a2.95 2.95 0 0 1-1.02.818 3 3 0 0 1-.955.281l-1.204.143zm4.601 6.51a2.967 2.967 0 0 1-3.969-1.363l-.975-1.995a3 3 0 0 1-.164-.409l-.213-.671.704.02a4.2 4.2 0 0 0 1.977-.429 4.2 4.2 0 0 0 1.801-1.675l.353-.609.459.534q.245.284.416.633l.975 1.995a2.97 2.97 0 0 1-1.364 3.969" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_alias_vector_icon.xml b/core/res/res/drawable/pointer_alias_vector_icon.xml new file mode 100644 index 000000000000..6057a2ebc5dc --- /dev/null +++ b/core/res/res/drawable/pointer_alias_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_alias_vector" + android:hotSpotX="11.5dp" + android:hotSpotY="11.5dp" /> diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml new file mode 100644 index 000000000000..1692e5e62a46 --- /dev/null +++ b/core/res/res/drawable/pointer_all_scroll_vector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.93 4.54a1.06 1.06 0 0 0-1.85 0L9.32 7.6c-.4.71.1 1.6.92 1.6h.82v1.86H9.2v-.84c0-.82-.88-1.33-1.6-.93l-3.06 1.76c-.7.41-.7 1.44 0 1.85l3.07 1.76c.7.4 1.6-.1 1.6-.93v-.79h1.86v1.87h-.82c-.81 0-1.33.88-.92 1.6l1.76 3.06c.4.71 1.44.71 1.85 0l1.75-3.07c.41-.7-.1-1.6-.92-1.6h-.82v-1.86h1.86v.8c0 .81.89 1.32 1.6.92l3.07-1.76c.7-.41.7-1.44 0-1.85L16.4 9.3c-.71-.4-1.6.1-1.6.93v.84h-1.86V9.2h.82c.82 0 1.33-.89.92-1.6l-1.75-3.06z" + android:fillColor="#000000"/> + <path + android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_all_scroll_vector_icon.xml b/core/res/res/drawable/pointer_all_scroll_vector_icon.xml new file mode 100644 index 000000000000..d64b99abb53e --- /dev/null +++ b/core/res/res/drawable/pointer_all_scroll_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_all_scroll_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml new file mode 100644 index 000000000000..562f0c05f662 --- /dev/null +++ b/core/res/res/drawable/pointer_arrow_vector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.34 11.18 6.77 4.02a1.78 1.78 0 0 0-1.88-.17c-.63.31-1 .91-1 1.6l.01 11.96c0 .9.6 1.46 1.15 1.67a1.74 1.74 0 0 0 1.98-.45l2.96-3.19c.3-.32.7-.52 1.13-.56l4.33-.47a1.8 1.8 0 0 0 .89-3.23z" + android:fillColor="#000000"/> + <path + android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/core/res/res/drawable/pointer_arrow_vector_icon.xml b/core/res/res/drawable/pointer_arrow_vector_icon.xml new file mode 100644 index 000000000000..b7a89924e417 --- /dev/null +++ b/core/res/res/drawable/pointer_arrow_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_arrow_vector" + android:hotSpotX="4.5dp" + android:hotSpotY="3.5dp" /> diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml new file mode 100644 index 000000000000..044a4f4014cb --- /dev/null +++ b/core/res/res/drawable/pointer_cell_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" /> + <path + android:fillColor="#000000" + android:pathData="M19 10.667h-5.668V5a1 1 0 0 0-1-1h-.667a1 1 0 0 0-1 1v5.667H5a1 1 0 0 0-1 1v.667a1 1 0 0 0 1 1h5.665V19a1 1 0 0 0 1 1h.667a1 1 0 0 0 1-1v-5.666H19a1 1 0 0 0 1-1v-.667a1 1 0 0 0-1-1" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_cell_vector_icon.xml b/core/res/res/drawable/pointer_cell_vector_icon.xml new file mode 100644 index 000000000000..9e0f63274d2d --- /dev/null +++ b/core/res/res/drawable/pointer_cell_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_cell_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml new file mode 100644 index 000000000000..8e954d290619 --- /dev/null +++ b/core/res/res/drawable/pointer_context_menu_vector.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" /> + <path android:fillColor="#FFFFFF" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" /> + </group> + <path + android:fillColor="#FFFFFF" + android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" /> + <path + android:fillColor="#000000" + android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" /> + <path + android:fillColor="#000000" + android:pathData="M19.475 3.461h-2.66c-.37 0-.67.3-.67.67v2.66c0 .37.3.67.67.67h2.66c.37 0 .67-.3.67-.67v-2.66a.67.67 0 0 0-.67-.67m-.3 3.062h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.868h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.885h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_context_menu_vector_icon.xml b/core/res/res/drawable/pointer_context_menu_vector_icon.xml new file mode 100644 index 000000000000..90f90431438b --- /dev/null +++ b/core/res/res/drawable/pointer_context_menu_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_context_menu_vector" + android:hotSpotX="4.5dp" + android:hotSpotY="3.5dp" /> diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml new file mode 100644 index 000000000000..b1e8995269a7 --- /dev/null +++ b/core/res/res/drawable/pointer_copy_vector.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" /> + <path android:fillColor="#FFFFFF" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" /> + </group> + <path + android:fillColor="#000000" + android:pathData="M18.485 10.884v1.739q.013.189.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.062 0 .123.007.185.009A4.4 4.4 0 0 1 13 6.5c0-.378.061-.739.149-1.09a1.97 1.97 0 0 0-1.659-.926c-.903 0-1.658.603-1.906 1.425a1.997 1.997 0 0 0-3.091 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.108c-.316.156-.645.29-.999.37" /> + <path + android:fillColor="#1FA54A" + android:pathData="M17.5 3C15.57 3 14 4.57 14 6.5s1.57 3.5 3.5 3.5S21 8.43 21 6.5 19.43 3 17.5 3m1.799 4H18v1.3a.5.5 0 0 1-1 0V7h-1.301a.5.5 0 0 1 0-1H17V4.7a.5.5 0 0 1 1 0V6h1.299a.5.5 0 0 1 0 1" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_copy_vector_icon.xml b/core/res/res/drawable/pointer_copy_vector_icon.xml new file mode 100644 index 000000000000..fe2db15a5c24 --- /dev/null +++ b/core/res/res/drawable/pointer_copy_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_copy_vector" + android:hotSpotX="8.5dp" + android:hotSpotY="7.5dp" /> diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml new file mode 100644 index 000000000000..b2e7e8a68615 --- /dev/null +++ b/core/res/res/drawable/pointer_crosshair_vector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" /> + <path + android:fillType="evenOdd" + android:fillColor="#000000" + android:pathData="M19.25 11.25h-6.5v-6.5a.75.75 0 0 0-1.5 0v6.5h-6.5a.75.75 0 0 0 0 1.5h6.5v6.5a.75.75 0 0 0 1.5 0v-6.5h6.5a.75.75 0 0 0 0-1.5" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_crosshair_vector_icon.xml b/core/res/res/drawable/pointer_crosshair_vector_icon.xml new file mode 100644 index 000000000000..d938514d2877 --- /dev/null +++ b/core/res/res/drawable/pointer_crosshair_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_crosshair_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml new file mode 100644 index 000000000000..7d9f048bbbb6 --- /dev/null +++ b/core/res/res/drawable/pointer_grab_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M19.442 14.581V7.562a1 1 0 0 0-2 0v2.997a5.7 5.7 0 0 0-1-.663v-5.21a1 1 0 0 0-2 0v4.615a5.5 5.5 0 0 0-1-.011V3a1 1 0 0 0-2 0v6.835a5.6 5.6 0 0 0-.996.623V4.625a1 1 0 0 0-2 0v8.955l-1.962-2.008a1.215 1.215 0 0 0-1.747 0 1.286 1.286 0 0 0 0 1.788l5.106 5.224q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.046 0 5.565-2.469 5.565-5.516z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_grab_vector_icon.xml b/core/res/res/drawable/pointer_grab_vector_icon.xml new file mode 100644 index 000000000000..6ff70825395c --- /dev/null +++ b/core/res/res/drawable/pointer_grab_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_grab_vector" + android:hotSpotX="9.5dp" + android:hotSpotY="4.5dp" /> diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml new file mode 100644 index 000000000000..9c9610366b6d --- /dev/null +++ b/core/res/res/drawable/pointer_grabbing_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M18.485 12.622V8.508a1 1 0 0 0-2 0v.259a5.6 5.6 0 0 0-1-.66v-.599a1 1 0 0 0-2 0v.008a5.6 5.6 0 0 0-.996-.009V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514a5 5 0 0 0-.012-.381" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_grabbing_vector_icon.xml b/core/res/res/drawable/pointer_grabbing_vector_icon.xml new file mode 100644 index 000000000000..903c69346d75 --- /dev/null +++ b/core/res/res/drawable/pointer_grabbing_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_grabbing_vector" + android:hotSpotX="8.5dp" + android:hotSpotY="7.5dp" /> diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml new file mode 100644 index 000000000000..79792f840c85 --- /dev/null +++ b/core/res/res/drawable/pointer_hand_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M19.496 10.999A.997.997 0 0 0 18.5 10a.995.995 0 0 0-.992.98v.644a.5.5 0 0 1-.498.5c-.014 0-.026-.007-.04-.008a.493.493 0 0 1-.457-.491v-1.02l-.004-.003V10c0-.552-.446-.999-.996-.999s-.996.447-.996.999v.008h-.001l-.005 1.003-.001.154a.5.5 0 0 1-.498.497h-.003a.5.5 0 0 1-.495-.502l.001-.159.006-1.227v-.796a.997.997 0 0 0-.996-.999.993.993 0 0 0-.988.96v2.185a.496.496 0 0 1-.989.037l-.011.007v-7.17a.997.997 0 0 0-.996-.999.997.997 0 0 0-.996.999V14.28l-1.95-2.002a1.21 1.21 0 0 0-1.74 0 1.286 1.286 0 0 0 0 1.786l5.084 5.219q.056.057.117.106A5.54 5.54 0 0 0 13.962 21c3.033 0 5.541-2.467 5.541-5.51a6 6 0 0 0-.008-.293z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_hand_vector_icon.xml b/core/res/res/drawable/pointer_hand_vector_icon.xml new file mode 100644 index 000000000000..c59c8dc8de44 --- /dev/null +++ b/core/res/res/drawable/pointer_hand_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_hand_vector" + android:hotSpotX="9.5dp" + android:hotSpotY="2.5dp" /> diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml new file mode 100644 index 000000000000..09f3e31473dd --- /dev/null +++ b/core/res/res/drawable/pointer_handwriting_vector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" /> + <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" /> + </group> + <path + android:fillColor="#000000" + android:pathData="M5 4c-.55 0-1 .45-1 1v14c0 .55.45 1 1 1s1-.45 1-1V5c0-.55-.45-1-1-1m14.41 3.51-1.42-1.42c-.39-.39-.9-.59-1.41-.59h-.01c-.51 0-1.02.2-1.41.59L8 13.25v4.25h4.25l7.16-7.16c.78-.78.78-2.05 0-2.83m-7.839 7.85-1.43-1.43 6.29-6.29h.01l1.42 1.43z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_handwriting_vector_icon.xml b/core/res/res/drawable/pointer_handwriting_vector_icon.xml new file mode 100644 index 000000000000..14a8700bbe70 --- /dev/null +++ b/core/res/res/drawable/pointer_handwriting_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_handwriting_vector" + android:hotSpotX="8.25dp" + android:hotSpotY="23.75dp" />
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml new file mode 100644 index 000000000000..6b7fd9f99a26 --- /dev/null +++ b/core/res/res/drawable/pointer_help_vector.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" /> + <path + android:fillColor="#000000" + android:pathData="M17.73 7.254a.55.55 0 0 0-.407.169.55.55 0 0 0-.169.407q0 .225.169.401a.55.55 0 0 0 .808 0 .56.56 0 0 0 .175-.413.53.53 0 0 0-.175-.394.56.56 0 0 0-.401-.17m1.202-4.288q-.413-.42-1.126-.419-.651 0-1.164.357a2.1 2.1 0 0 0-.751.945l.864.326q.15-.363.407-.551a.93.93 0 0 1 .557-.188q.313 0 .526.182c.213.182.213.286.213.495q0 .226-.144.457a1.4 1.4 0 0 1-.394.432q-.35.3-.538.657c-.125.238-.187.485-.187.86l.926.06q0-.3.081-.57t.469-.595q.363-.276.519-.601t.156-.726q-.002-.701-.414-1.121" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_help_vector_icon.xml b/core/res/res/drawable/pointer_help_vector_icon.xml new file mode 100644 index 000000000000..78cc3e961c94 --- /dev/null +++ b/core/res/res/drawable/pointer_help_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_help_vector" + android:hotSpotX="4.5dp" + android:hotSpotY="3.5dp" /> diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml new file mode 100644 index 000000000000..d1aea9eacf3f --- /dev/null +++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" /> + <path + android:fillColor="#000000" + android:pathData="M19.465 11.052 16.4 9.294a1.065 1.065 0 0 0-1.596.922v.843H9.195v-.843c0-.818-.886-1.33-1.596-.922l-3.065 1.758a1.063 1.063 0 0 0 0 1.845l3.065 1.758a1.065 1.065 0 0 0 1.596-.922v-.791h5.609v.791c0 .818.886 1.33 1.596.922l3.065-1.758a1.063 1.063 0 0 0 0-1.845" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml new file mode 100644 index 000000000000..cee5f9191f18 --- /dev/null +++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_horizontal_double_arrow_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml new file mode 100644 index 000000000000..3a38babd12d2 --- /dev/null +++ b/core/res/res/drawable/pointer_nodrop_vector.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" /> + <path android:fillColor="#FFFFFF" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" /> + </group> + <path + android:fillColor="#000000" + android:pathData="M18.485 10.932v1.69q.013.188.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.046 0 .09.006.135.007a4.5 4.5 0 0 1-.117-.995c0-.399.068-.779.165-1.148a1.97 1.97 0 0 0-1.629-.867c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-1.091-.327 2 2 0 0 0-2 2v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.056a4.5 4.5 0 0 1-.999.366" /> + <path + android:fillColor="#B22A25" + android:pathData="M17.5 3C15.57 3 14 4.57 14 6.5s1.57 3.5 3.5 3.5S21 8.43 21 6.5 19.43 3 17.5 3m0 6A2.5 2.5 0 0 1 15 6.5c0-.525.164-1.011.441-1.413l3.472 3.472A2.5 2.5 0 0 1 17.5 9m2.105-1.163-3.442-3.442A2.5 2.5 0 0 1 17.5 4C18.879 4 20 5.121 20 6.5c0 .493-.148.95-.395 1.337" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_nodrop_vector_icon.xml b/core/res/res/drawable/pointer_nodrop_vector_icon.xml new file mode 100644 index 000000000000..ceba0029133e --- /dev/null +++ b/core/res/res/drawable/pointer_nodrop_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_nodrop_vector" + android:hotSpotX="8.5dp" + android:hotSpotY="7.5dp" /> diff --git a/core/res/res/drawable/pointer_spot_anchor_vector.xml b/core/res/res/drawable/pointer_spot_anchor_vector.xml new file mode 100644 index 000000000000..54de2aecb4ce --- /dev/null +++ b/core/res/res/drawable/pointer_spot_anchor_vector.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" /> + <path android:fillColor="#ADC6E7" android:pathData="M12 5c-3.859 0-7 3.14-7 7s3.141 7 7 7 7-3.141 7-7-3.141-7-7-7m0 13c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6" /> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml b/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml new file mode 100644 index 000000000000..83b767c7f7ec --- /dev/null +++ b/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_anchor_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_spot_hover_vector.xml b/core/res/res/drawable/pointer_spot_hover_vector.xml new file mode 100644 index 000000000000..ef596c470480 --- /dev/null +++ b/core/res/res/drawable/pointer_spot_hover_vector.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" /> + <path android:fillColor="#ADC6E7" android:pathData="M12 7c-2.757 0-5 2.243-5 5s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5m0 9c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4" /> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_spot_hover_vector_icon.xml b/core/res/res/drawable/pointer_spot_hover_vector_icon.xml new file mode 100644 index 000000000000..f8929586bc04 --- /dev/null +++ b/core/res/res/drawable/pointer_spot_hover_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_hover_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_spot_touch_vector.xml b/core/res/res/drawable/pointer_spot_touch_vector.xml new file mode 100644 index 000000000000..afd2956858fa --- /dev/null +++ b/core/res/res/drawable/pointer_spot_touch_vector.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#ADC6E7" + android:pathData="M21 12c0-4.963-4.038-9-9-9s-9 4.037-9 9 4.038 9 9 9 9-4.037 9-9m-9 8c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_spot_touch_vector_icon.xml b/core/res/res/drawable/pointer_spot_touch_vector_icon.xml new file mode 100644 index 000000000000..7b9693828c33 --- /dev/null +++ b/core/res/res/drawable/pointer_spot_touch_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_spot_touch_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml new file mode 100644 index 000000000000..9e44f28f5779 --- /dev/null +++ b/core/res/res/drawable/pointer_text_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M12 2c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2s2-.897 2-2V4c0-1.103-.897-2-2-2m1 16a1.001 1.001 0 0 1-2 0V4a1.001 1.001 0 0 1 2 0z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_text_vector_icon.xml b/core/res/res/drawable/pointer_text_vector_icon.xml new file mode 100644 index 000000000000..b03f8da376ce --- /dev/null +++ b/core/res/res/drawable/pointer_text_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_text_vector" + android:hotSpotX="12dp" + android:hotSpotY="11dp" /> diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml new file mode 100644 index 000000000000..e5d5301ce009 --- /dev/null +++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" /> + <path + android:fillType="evenOdd" + android:fillColor="#000000" + android:pathData="M6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.476l.924 3.41a1.063 1.063 0 0 1-1.304 1.304l-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml new file mode 100644 index 000000000000..7fd2a7f1059b --- /dev/null +++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_top_left_diagonal_double_arrow_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml new file mode 100644 index 000000000000..e6f7aafe2e6b --- /dev/null +++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" /> + <path + android:fillColor="#000000" + android:pathData="m16.626 6.069-3.41.924a1.065 1.065 0 0 0-.476 1.781l.579.579-3.966 3.966-.579-.579a1.066 1.066 0 0 0-1.781.477l-.924 3.41a1.063 1.063 0 0 0 1.304 1.304l3.41-.924a1.065 1.065 0 0 0 .476-1.781l-.578-.578 3.966-3.966.577.577a1.066 1.066 0 0 0 1.781-.476l.924-3.41a1.062 1.062 0 0 0-1.303-1.304" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml new file mode 100644 index 000000000000..d2516b16fb71 --- /dev/null +++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_top_right_diagonal_double_arrow_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml new file mode 100644 index 000000000000..6ffcfefead3d --- /dev/null +++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" /> + <path + android:fillColor="#000000" + android:pathData="M13.761 14.805h-.817v-5.61h.817c.818 0 1.33-.886.922-1.596l-1.758-3.065a1.063 1.063 0 0 0-1.845 0L9.323 7.599c-.407.71.104 1.596.922 1.596h.818v5.609h-.818c-.818 0-1.33.886-.922 1.596l1.758 3.065a1.063 1.063 0 0 0 1.845 0l1.758-3.065a1.065 1.065 0 0 0-.923-1.595" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml new file mode 100644 index 000000000000..64f342467915 --- /dev/null +++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_vertical_double_arrow_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml new file mode 100644 index 000000000000..72f40ccfc82b --- /dev/null +++ b/core/res/res/drawable/pointer_vertical_text_vector.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000000" + android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M19 10H5c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2m0 3H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_vertical_text_vector_icon.xml b/core/res/res/drawable/pointer_vertical_text_vector_icon.xml new file mode 100644 index 000000000000..afb225aa8999 --- /dev/null +++ b/core/res/res/drawable/pointer_vertical_text_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_vertical_text_vector" + android:hotSpotX="12dp" + android:hotSpotY="12dp" /> diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml new file mode 100644 index 000000000000..89216662964b --- /dev/null +++ b/core/res/res/drawable/pointer_zoom_in_vector.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> + <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" /> + </group> + <group> + <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> + <path android:fillColor="#000000" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" /> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_zoom_in_vector_icon.xml b/core/res/res/drawable/pointer_zoom_in_vector_icon.xml new file mode 100644 index 000000000000..fcc0c2867ae0 --- /dev/null +++ b/core/res/res/drawable/pointer_zoom_in_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_zoom_in_vector" + android:hotSpotX="10.5dp" + android:hotSpotY="9.5dp" /> diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml new file mode 100644 index 000000000000..815ce0ebe9b4 --- /dev/null +++ b/core/res/res/drawable/pointer_zoom_out_vector.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group> + <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> + <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" /> + </group> + <group> + <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> + <path android:fillColor="#000000" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" /> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_zoom_out_vector_icon.xml b/core/res/res/drawable/pointer_zoom_out_vector_icon.xml new file mode 100644 index 000000000000..37f4e793608c --- /dev/null +++ b/core/res/res/drawable/pointer_zoom_out_vector_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_zoom_out_vector" + android:hotSpotX="10.5dp" + android:hotSpotY="9.5dp" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d91094042402..47410125fd65 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -3278,6 +3278,31 @@ <p> By default, the behavior is configured by the same attribute in application. --> <attr name="enableOnBackInvokedCallback" format="boolean"/> + + <!-- Specifies permissions necessary to launch this activity via + {@link android.content.Context#startActivity} when passing content URIs. The default + value is {@code none}, meaning no specific permissions are required. Setting this + attribute restricts activity invocation based on the invoker's permissions. If the + invoker doesn't have the required permissions, the activity start will be denied via a + {@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}. + @FlaggedApi("android.security.content_uri_permission_apis") --> + <attr name="requireContentUriPermissionFromCaller" format="string"> + <!-- Default, no specific permissions are required. --> + <enum name="none" value="0" /> + <!-- Enforces the invoker to have read access to the passed content URIs. --> + <enum name="read" value="1" /> + <!-- Enforces the invoker to have write access to the passed content URIs. --> + <enum name="write" value="2" /> + <!-- Enforces the invoker to have either read or write access to the passed content + URIs. --> + <enum name="readOrWrite" value="3" /> + <!-- Enforces the invoker to have both read and write access to the passed content + URIs. --> + <enum name="readAndWrite" value="4" /> + </attr> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 4799c3769b0f..f9cf28c1cd04 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -149,6 +149,8 @@ <public name="autoTransact"/> <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") --> <public name="windowOptOutEdgeToEdgeEnforcement"/> + <!-- @FlaggedApi("android.security.content_uri_permission_apis") --> + <public name="requireContentUriPermissionFromCaller" /> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 22d028cb079e..adf8d9f95b52 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1460,6 +1460,43 @@ please see styles_device_defaults.xml. </style> <!-- @hide --> + <style name="VectorPointer"> + <item name="pointerIconArrow">@drawable/pointer_arrow_vector_icon</item> + <item name="pointerIconSpotHover">@drawable/pointer_spot_hover_vector_icon</item> + <item name="pointerIconSpotTouch">@drawable/pointer_spot_touch_vector_icon</item> + <item name="pointerIconSpotAnchor">@drawable/pointer_spot_anchor_vector_icon</item> + <item name="pointerIconHand">@drawable/pointer_hand_vector_icon</item> + <item name="pointerIconContextMenu">@drawable/pointer_context_menu_vector_icon</item> + <item name="pointerIconHelp">@drawable/pointer_help_vector_icon</item> + <item name="pointerIconWait">@drawable/pointer_wait_icon</item> + <item name="pointerIconCell">@drawable/pointer_cell_vector_icon</item> + <item name="pointerIconCrosshair">@drawable/pointer_crosshair_vector_icon</item> + <item name="pointerIconText">@drawable/pointer_text_vector_icon</item> + <item name="pointerIconVerticalText">@drawable/pointer_vertical_text_vector_icon</item> + <item name="pointerIconAlias">@drawable/pointer_alias_vector_icon</item> + <item name="pointerIconCopy">@drawable/pointer_copy_vector_icon</item> + <item name="pointerIconAllScroll">@drawable/pointer_all_scroll_vector_icon</item> + <item name="pointerIconNodrop">@drawable/pointer_nodrop_vector_icon</item> + <item name="pointerIconHorizontalDoubleArrow"> + @drawable/pointer_horizontal_double_arrow_vector_icon + </item> + <item name="pointerIconVerticalDoubleArrow"> + @drawable/pointer_vertical_double_arrow_vector_icon + </item> + <item name="pointerIconTopRightDiagonalDoubleArrow"> + @drawable/pointer_top_right_diagonal_double_arrow_vector_icon + </item> + <item name="pointerIconTopLeftDiagonalDoubleArrow"> + @drawable/pointer_top_left_diagonal_double_arrow_vector_icon + </item> + <item name="pointerIconZoomIn">@drawable/pointer_zoom_in_vector_icon</item> + <item name="pointerIconZoomOut">@drawable/pointer_zoom_out_vector_icon</item> + <item name="pointerIconGrab">@drawable/pointer_grab_vector_icon</item> + <item name="pointerIconGrabbing">@drawable/pointer_grabbing_vector_icon</item> + <item name="pointerIconHandwriting">@drawable/pointer_handwriting_vector_icon</item> + </style> + + <!-- @hide --> <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless"> <item name="minHeight">?attr/listPreferredItemHeightSmall</item> <item name="textAppearance">?attr/textAppearanceListItemSmall</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 58427b19fac4..3df7570c818e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1681,6 +1681,7 @@ <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" /> <java-symbol type="style" name="Pointer" /> <java-symbol type="style" name="LargePointer" /> + <java-symbol type="style" name="VectorPointer" /> <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" /> <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" /> diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 52e996cab3ed..2d117f7217f5 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -829,6 +829,34 @@ public class ViewRootImplTest { } /** + * A View should either vote a frame rate or a frame rate category instead of both. + */ + @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void votePreferredFrameRate_voteFrameRateOnly() { + View view = new View(sContext); + float frameRate = 20; + attachViewToWindow(view); + sInstrumentation.waitForIdleSync(); + + ViewRootImpl viewRootImpl = view.getViewRootImpl(); + sInstrumentation.runOnMainSync(() -> { + assertEquals(viewRootImpl.getPreferredFrameRateCategory(), + FRAME_RATE_CATEGORY_NO_PREFERENCE); + + view.setRequestedFrameRate(frameRate); + view.invalidate(); + assertEquals(viewRootImpl.getPreferredFrameRateCategory(), + FRAME_RATE_CATEGORY_NO_PREFERENCE); + assertEquals(viewRootImpl.getPreferredFrameRate(), frameRate, 0.1); + + view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW); + view.invalidate(); + assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW); + }); + } + + /** * Test the logic of infrequent layer: * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100. * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100. diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 13d38d2ff2c6..9d1e5074dd3e 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -124,6 +124,10 @@ <group gid="security_log_writer" /> </permission> + <permission name="android.permission.MANAGE_VIRTUAL_MACHINE"> + <group gid="virtualmachine" /> + </permission> + <!-- These are permissions that were mapped to gids but we need to keep them here until an upgrade from L to the current version is to be supported. These permissions are built-in diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml index bf6094469215..782327713fdc 100644 --- a/data/etc/preinstalled-packages-platform.xml +++ b/data/etc/preinstalled-packages-platform.xml @@ -108,6 +108,7 @@ to pre-existing users, but cannot uninstall pre-existing system packages from pr <install-in user-type="FULL" /> <install-in user-type="PROFILE" /> <do-not-install-in user-type="android.os.usertype.profile.CLONE" /> + <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" /> </install-in-user-type> <!-- Settings (Settings app) --> diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt index e4bf4c26dc7d..a926fcb8d3d1 100644 --- a/ktfmt_includes.txt +++ b/ktfmt_includes.txt @@ -507,7 +507,7 @@ -packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt -packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS index e346b51a4f19..0c4fd140780e 100644 --- a/libs/WindowManager/Shell/OWNERS +++ b/libs/WindowManager/Shell/OWNERS @@ -2,3 +2,4 @@ xutan@google.com # Give submodule owners in shell resource approval per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com +per-file res*/*/tv_*.xml = bronger@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java index 160f922dd928..55982dca79b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java @@ -310,12 +310,16 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top); float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width()); float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height()); - float alpha = mapRange(progress, mEnteringProgress, 1.0f); - + float alpha = mapRange(progress, getPreCommitEnteringAlpha(), 1.0f); mEnteringRect.set(left, top, left + width, top + height); applyTransform(mEnteringTarget.leash, mEnteringRect, alpha); } + private float getPreCommitEnteringAlpha() { + return Math.max(smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress), + MIN_WINDOW_ALPHA); + } + private float getEnteringProgress() { return mEnteringProgress * SCALE_FACTOR; } @@ -325,9 +329,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { if (mEnteringTarget != null && mEnteringTarget.leash != null) { transformWithProgress( mEnteringProgress, - Math.max( - smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress), - MIN_WINDOW_ALPHA), /* alpha */ + getPreCommitEnteringAlpha(), mEnteringTarget.leash, mEnteringRect, -mWindowXShift, @@ -336,6 +338,11 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { } } + private float getPreCommitLeavingAlpha() { + return Math.max(1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress), + MIN_WINDOW_ALPHA); + } + private float getLeavingProgress() { return mLeavingProgress * SCALE_FACTOR; } @@ -345,9 +352,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { if (mClosingTarget != null && mClosingTarget.leash != null) { transformWithProgress( mLeavingProgress, - Math.max( - 1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress), - MIN_WINDOW_ALPHA), + getPreCommitLeavingAlpha(), mClosingTarget.leash, mClosingRect, 0, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 7a4ad0a56022..da530d740d48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -53,8 +53,6 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.bubbles.BubbleInfo; -import com.android.wm.shell.taskview.TaskView; -import com.android.wm.shell.taskview.TaskViewTaskController; import java.io.PrintWriter; import java.util.List; @@ -403,13 +401,9 @@ public class Bubble implements BubbleViewProvider { * Returns the existing {@link #mBubbleTaskView} if it's not {@code null}. Otherwise a new * instance of {@link BubbleTaskView} is created. */ - public BubbleTaskView getOrCreateBubbleTaskView(Context context, BubbleController controller) { + public BubbleTaskView getOrCreateBubbleTaskView(BubbleTaskViewFactory taskViewFactory) { if (mBubbleTaskView == null) { - TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context, - controller.getTaskOrganizer(), - controller.getTaskViewTransitions(), controller.getSyncTransactionQueue()); - TaskView taskView = new TaskView(context, taskViewTaskController); - mBubbleTaskView = new BubbleTaskView(taskView, controller.getMainExecutor()); + mBubbleTaskView = taskViewFactory.create(); } return mBubbleTaskView; } @@ -514,14 +508,18 @@ public class Bubble implements BubbleViewProvider { * * @param callback the callback to notify one the bubble is ready to be displayed. * @param context the context for the bubble. - * @param controller the bubble controller. + * @param expandedViewManager the bubble expanded view manager. + * @param taskViewFactory the task view factory used to create the task view for the bubble. + * @param positioner the bubble positioner. * @param stackView the view the bubble is added to, iff showing as floating. * @param layerView the layer the bubble is added to, iff showing in the bubble bar. - * @param iconFactory the icon factory use to create images for the bubble. + * @param iconFactory the icon factory used to create images for the bubble. */ void inflate(BubbleViewInfoTask.Callback callback, Context context, - BubbleController controller, + BubbleExpandedViewManager expandedViewManager, + BubbleTaskViewFactory taskViewFactory, + BubblePositioner positioner, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory iconFactory, @@ -531,7 +529,9 @@ public class Bubble implements BubbleViewProvider { } mInflationTask = new BubbleViewInfoTask(this, context, - controller, + expandedViewManager, + taskViewFactory, + positioner, stackView, layerView, iconFactory, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 0aa89598cd10..5c6f73f0a4a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -113,6 +113,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.taskview.TaskView; +import com.android.wm.shell.taskview.TaskViewTaskController; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; @@ -190,6 +191,8 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellCommandHandler mShellCommandHandler; private final IWindowManager mWmService; private final BubbleProperties mBubbleProperties; + private final BubbleTaskViewFactory mBubbleTaskViewFactory; + private final BubbleExpandedViewManager mExpandedViewManager; // Used to post to main UI thread private final ShellExecutor mMainExecutor; @@ -333,6 +336,16 @@ public class BubbleController implements ConfigurationChangeListener, mWmService = wmService; mBubbleProperties = bubbleProperties; shellInit.addInitCallback(this::onInit, this); + mBubbleTaskViewFactory = new BubbleTaskViewFactory() { + @Override + public BubbleTaskView create() { + TaskViewTaskController taskViewTaskController = new TaskViewTaskController( + context, organizer, taskViewTransitions, syncQueue); + TaskView taskView = new TaskView(context, taskViewTaskController); + return new BubbleTaskView(taskView, mainExecutor); + } + }; + mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this); } private void registerOneHandedState(OneHandedController oneHanded) { @@ -802,7 +815,13 @@ public class BubbleController implements ConfigurationChangeListener, try { mAddedToWindowManager = true; registerBroadcastReceiver(); - mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar()); + if (isShowingAsBubbleBar()) { + mBubbleData.getOverflow().initializeForBubbleBar( + mExpandedViewManager, mBubblePositioner); + } else { + mBubbleData.getOverflow().initialize( + mExpandedViewManager, mStackView, mBubblePositioner); + } // (TODO: b/273314541) some duplication in the inset listener if (isShowingAsBubbleBar()) { mWindowManager.addView(mLayerView, mWmLayoutParams); @@ -984,7 +1003,9 @@ public class BubbleController implements ConfigurationChangeListener, for (Bubble b : mBubbleData.getBubbles()) { b.inflate(null /* callback */, mContext, - this, + mExpandedViewManager, + mBubbleTaskViewFactory, + mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, @@ -993,7 +1014,9 @@ public class BubbleController implements ConfigurationChangeListener, for (Bubble b : mBubbleData.getOverflowBubbles()) { b.inflate(null /* callback */, mContext, - this, + mExpandedViewManager, + mBubbleTaskViewFactory, + mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, @@ -1377,7 +1400,9 @@ public class BubbleController implements ConfigurationChangeListener, bubble.inflate( (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble), mContext, - this, + mExpandedViewManager, + mBubbleTaskViewFactory, + mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, @@ -1431,7 +1456,9 @@ public class BubbleController implements ConfigurationChangeListener, Bubble bubble = mBubbleData.getBubbles().get(i); bubble.inflate(callback, mContext, - this, + mExpandedViewManager, + mBubbleTaskViewFactory, + mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, @@ -1506,8 +1533,14 @@ public class BubbleController implements ConfigurationChangeListener, // Lazy init stack view when a bubble is created ensureBubbleViewsAndWindowCreated(); bubble.setInflateSynchronously(mInflateSynchronously); - bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), - mContext, this, mStackView, mLayerView, + bubble.inflate( + b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), + mContext, + mExpandedViewManager, + mBubbleTaskViewFactory, + mBubblePositioner, + mStackView, + mLayerView, mBubbleIconFactory, false /* skipInflation */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6d3f0c32bcbb..6c2f925119f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -37,10 +37,8 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FrameworkStatsLog; -import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubbles.DismissReason; -import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.common.bubbles.RemovedBubble; @@ -180,7 +178,7 @@ public class BubbleData { * This interface reports changes to the state and appearance of bubbles which should be applied * as necessary to the UI. */ - interface Listener { + public interface Listener { /** Reports changes have have occurred as a result of the most recent operation. */ void applyUpdate(Update update); } @@ -419,8 +417,10 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. - * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleController, BubbleStackView, - * BubbleBarLayerView, BubbleIconFactory, boolean) + * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager, + * BubbleTaskViewFactory, BubblePositioner, BubbleStackView, + * com.android.wm.shell.bubbles.bar.BubbleBarLayerView, + * com.android.launcher3.icons.BubbleIconFactory, boolean) */ void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) { mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 088660e4e8dc..df9ba63b4cc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -184,7 +184,7 @@ public class BubbleExpandedView extends LinearLayout { private boolean mIsOverflow; private boolean mIsClipping; - private BubbleController mController; + private BubbleExpandedViewManager mManager; private BubbleStackView mStackView; private BubblePositioner mPositioner; @@ -261,7 +261,7 @@ public class BubbleExpandedView extends LinearLayout { // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); - mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); + mManager.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); } }); mInitialized = true; @@ -281,7 +281,7 @@ public class BubbleExpandedView extends LinearLayout { if (mBubble != null && mBubble.isAppBubble()) { // Let the controller know sooner what the taskId is. - mController.setAppBubbleTaskId(mBubble.getKey(), mTaskId); + mManager.setAppBubbleTaskId(mBubble.getKey(), mTaskId); } // With the task org, the taskAppeared callback will only happen once the task has @@ -301,7 +301,7 @@ public class BubbleExpandedView extends LinearLayout { ProtoLog.d(WM_SHELL_BUBBLES, "onTaskRemovalStarted: taskId=%d bubble=%s", taskId, getBubbleKey()); if (mBubble != null) { - mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); + mManager.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); } if (mTaskView != null) { // Release the surface @@ -421,17 +421,20 @@ public class BubbleExpandedView extends LinearLayout { * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need * to be called after view inflate. */ - void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow, + void initialize(BubbleExpandedViewManager expandedViewManager, + BubbleStackView stackView, + BubblePositioner positioner, + boolean isOverflow, @Nullable BubbleTaskView bubbleTaskView) { - mController = controller; + mManager = expandedViewManager; mStackView = stackView; mIsOverflow = isOverflow; - mPositioner = mController.getPositioner(); + mPositioner = positioner; if (mIsOverflow) { mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate( R.layout.bubble_overflow_container, null /* root */); - mOverflowView.setBubbleController(mController); + mOverflowView.initialize(expandedViewManager, positioner); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); mExpandedViewContainer.addView(mOverflowView, lp); mExpandedViewContainer.setLayoutParams( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt new file mode 100644 index 000000000000..b0d3cc4a5d5c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles + +/** Manager interface for bubble expanded views. */ +interface BubbleExpandedViewManager { + + val overflowBubbles: List<Bubble> + fun setOverflowListener(listener: BubbleData.Listener) + fun collapseStack() + fun updateWindowFlagsForBackpress(intercept: Boolean) + fun promoteBubbleFromOverflow(bubble: Bubble) + fun removeBubble(key: String, reason: Int) + fun dismissBubble(bubble: Bubble, reason: Int) + fun setAppBubbleTaskId(key: String, taskId: Int) + fun isStackExpanded(): Boolean + fun isShowingAsBubbleBar(): Boolean + + companion object { + /** + * Convenience function for creating a [BubbleExpandedViewManager] that delegates to the + * given `controller`. + */ + @JvmStatic + fun fromBubbleController(controller: BubbleController): BubbleExpandedViewManager { + return object : BubbleExpandedViewManager { + + override val overflowBubbles: List<Bubble> + get() = controller.overflowBubbles + + override fun setOverflowListener(listener: BubbleData.Listener) { + controller.setOverflowListener(listener) + } + + override fun collapseStack() { + controller.collapseStack() + } + + override fun updateWindowFlagsForBackpress(intercept: Boolean) { + controller.updateWindowFlagsForBackpress(intercept) + } + + override fun promoteBubbleFromOverflow(bubble: Bubble) { + controller.promoteBubbleFromOverflow(bubble) + } + + override fun removeBubble(key: String, reason: Int) { + controller.removeBubble(key, reason) + } + + override fun dismissBubble(bubble: Bubble, reason: Int) { + controller.dismissBubble(bubble, reason) + } + + override fun setAppBubbleTaskId(key: String, taskId: Int) { + controller.setAppBubbleTaskId(key, taskId) + } + + override fun isStackExpanded(): Boolean = controller.isStackExpanded + + override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index e5d9acedc903..f32974e1765d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -56,19 +56,32 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl } /** Call before use and again if cleanUpExpandedState was called. */ - fun initialize(controller: BubbleController, forBubbleBar: Boolean) { - if (forBubbleBar) { - createBubbleBarExpandedView() - .initialize(controller, /* isOverflow= */ true, /* bubbleTaskView= */ null) - } else { - createExpandedView() + fun initialize( + expandedViewManager: BubbleExpandedViewManager, + stackView: BubbleStackView, + positioner: BubblePositioner + ) { + createExpandedView() .initialize( - controller, - controller.stackView, + expandedViewManager, + stackView, + positioner, /* isOverflow= */ true, /* bubbleTaskView= */ null ) - } + } + + fun initializeForBubbleBar( + expandedViewManager: BubbleExpandedViewManager, + positioner: BubblePositioner + ) { + createBubbleBarExpandedView() + .initialize( + expandedViewManager, + positioner, + /* isOverflow= */ true, + /* bubbleTaskView= */ null + ) } fun cleanUpExpandedState() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java index 70cdc82c47ad..b06de4f4002c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java @@ -62,7 +62,8 @@ public class BubbleOverflowContainerView extends LinearLayout { private ImageView mEmptyStateImage; private int mHorizontalMargin; private int mVerticalMargin; - private BubbleController mController; + private BubbleExpandedViewManager mExpandedViewManager; + private BubblePositioner mPositioner; private BubbleOverflowAdapter mAdapter; private RecyclerView mRecyclerView; private List<Bubble> mOverflowBubbles = new ArrayList<>(); @@ -70,7 +71,7 @@ public class BubbleOverflowContainerView extends LinearLayout { private View.OnKeyListener mKeyListener = (view, i, keyEvent) -> { if (keyEvent.getAction() == KeyEvent.ACTION_UP && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) { - mController.collapseStack(); + mExpandedViewManager.collapseStack(); return true; } return false; @@ -126,8 +127,11 @@ public class BubbleOverflowContainerView extends LinearLayout { setFocusableInTouchMode(true); } - public void setBubbleController(BubbleController controller) { - mController = controller; + /** Initializes the view. Must be called after creation. */ + public void initialize(BubbleExpandedViewManager expandedViewManager, + BubblePositioner positioner) { + mExpandedViewManager = expandedViewManager; + mPositioner = positioner; } public void show() { @@ -149,9 +153,9 @@ public class BubbleOverflowContainerView extends LinearLayout { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (mController != null) { + if (mExpandedViewManager != null) { // For the overflow to get key events (e.g. back press) we need to adjust the flags - mController.updateWindowFlagsForBackpress(true); + mExpandedViewManager.updateWindowFlagsForBackpress(true); } setOnKeyListener(mKeyListener); } @@ -159,8 +163,8 @@ public class BubbleOverflowContainerView extends LinearLayout { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mController != null) { - mController.updateWindowFlagsForBackpress(false); + if (mExpandedViewManager != null) { + mExpandedViewManager.updateWindowFlagsForBackpress(false); } setOnKeyListener(null); } @@ -177,15 +181,15 @@ public class BubbleOverflowContainerView extends LinearLayout { mRecyclerView.addItemDecoration(new OverflowItemDecoration()); } mAdapter = new BubbleOverflowAdapter(getContext(), mOverflowBubbles, - mController::promoteBubbleFromOverflow, - mController.getPositioner()); + mExpandedViewManager::promoteBubbleFromOverflow, + mPositioner); mRecyclerView.setAdapter(mAdapter); mOverflowBubbles.clear(); - mOverflowBubbles.addAll(mController.getOverflowBubbles()); + mOverflowBubbles.addAll(mExpandedViewManager.getOverflowBubbles()); mAdapter.notifyDataSetChanged(); - mController.setOverflowListener(mDataListener); + mExpandedViewManager.setOverflowListener(mDataListener); updateEmptyStateVisibility(); updateTheme(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.kt new file mode 100644 index 000000000000..230626f49c51 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.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.wm.shell.bubbles + +/** Factory for creating [BubbleTaskView]s. */ +fun interface BubbleTaskViewFactory { + /** Creates a new instance of [BubbleTaskView]. */ + fun create(): BubbleTaskView +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 530ec5a6c9fe..21b70b8e32da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -62,7 +62,7 @@ public class BubbleTaskViewHelper { } private final Context mContext; - private final BubbleController mController; + private final BubbleExpandedViewManager mExpandedViewManager; private final BubbleTaskViewHelper.Listener mListener; private final View mParentView; @@ -142,7 +142,8 @@ public class BubbleTaskViewHelper { // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); - mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); + mExpandedViewManager.removeBubble( + getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); } mInitialized = true; }); @@ -175,7 +176,7 @@ public class BubbleTaskViewHelper { ProtoLog.d(WM_SHELL_BUBBLES, "onTaskRemovalStarted: taskId=%d bubble=%s", taskId, getBubbleKey()); if (mBubble != null) { - mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); + mExpandedViewManager.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); } if (mTaskView != null) { mTaskView.release(); @@ -186,19 +187,19 @@ public class BubbleTaskViewHelper { @Override public void onBackPressedOnTaskRoot(int taskId) { - if (mTaskId == taskId && mController.isStackExpanded()) { + if (mTaskId == taskId && mExpandedViewManager.isStackExpanded()) { mListener.onBackPressed(); } } }; public BubbleTaskViewHelper(Context context, - BubbleController controller, + BubbleExpandedViewManager expandedViewManager, BubbleTaskViewHelper.Listener listener, BubbleTaskView bubbleTaskView, View parent) { mContext = context; - mController = controller; + mExpandedViewManager = expandedViewManager; mListener = listener; mParentView = parent; mTaskView = bubbleTaskView.getTaskView(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 5fc10a9f6b69..69119cf4338e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -70,7 +70,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask private Bubble mBubble; private WeakReference<Context> mContext; - private WeakReference<BubbleController> mController; + private WeakReference<BubbleExpandedViewManager> mExpandedViewManager; + private WeakReference<BubbleTaskViewFactory> mTaskViewFactory; + private WeakReference<BubblePositioner> mPositioner; private WeakReference<BubbleStackView> mStackView; private WeakReference<BubbleBarLayerView> mLayerView; private BubbleIconFactory mIconFactory; @@ -84,7 +86,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask */ BubbleViewInfoTask(Bubble b, Context context, - BubbleController controller, + BubbleExpandedViewManager expandedViewManager, + BubbleTaskViewFactory taskViewFactory, + BubblePositioner positioner, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory factory, @@ -93,7 +97,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask Executor mainExecutor) { mBubble = b; mContext = new WeakReference<>(context); - mController = new WeakReference<>(controller); + mExpandedViewManager = new WeakReference<>(expandedViewManager); + mTaskViewFactory = new WeakReference<>(taskViewFactory); + mPositioner = new WeakReference<>(positioner); mStackView = new WeakReference<>(stackView); mLayerView = new WeakReference<>(layerView); mIconFactory = factory; @@ -109,11 +115,13 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask return null; } if (mLayerView.get() != null) { - return BubbleViewInfo.populateForBubbleBar(mContext.get(), mController.get(), - mLayerView.get(), mIconFactory, mBubble, mSkipInflation); + return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(), + mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory, + mBubble, mSkipInflation); } else { - return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(), - mIconFactory, mBubble, mSkipInflation); + return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(), + mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory, + mBubble, mSkipInflation); } } @@ -135,7 +143,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } private boolean verifyState() { - if (mController.get().isShowingAsBubbleBar()) { + if (mExpandedViewManager.get().isShowingAsBubbleBar()) { return mLayerView.get() != null; } else { return mStackView.get() != null; @@ -167,18 +175,23 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask Bitmap badgeBitmap; @Nullable - public static BubbleViewInfo populateForBubbleBar(Context c, BubbleController controller, - BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b, + public static BubbleViewInfo populateForBubbleBar(Context c, + BubbleExpandedViewManager expandedViewManager, + BubbleTaskViewFactory taskViewFactory, + BubblePositioner positioner, + BubbleBarLayerView layerView, + BubbleIconFactory iconFactory, + Bubble b, boolean skipInflation) { BubbleViewInfo info = new BubbleViewInfo(); if (!skipInflation && !b.isInflated()) { - BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller); + BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory); LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); info.bubbleBarExpandedView.initialize( - controller, false /* isOverflow */, bubbleTaskView); + expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { @@ -191,8 +204,13 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @VisibleForTesting @Nullable - public static BubbleViewInfo populate(Context c, BubbleController controller, - BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b, + public static BubbleViewInfo populate(Context c, + BubbleExpandedViewManager expandedViewManager, + BubbleTaskViewFactory taskViewFactory, + BubblePositioner positioner, + BubbleStackView stackView, + BubbleIconFactory iconFactory, + Bubble b, boolean skipInflation) { BubbleViewInfo info = new BubbleViewInfo(); @@ -201,13 +219,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask LayoutInflater inflater = LayoutInflater.from(c); info.imageView = (BadgedImageView) inflater.inflate( R.layout.bubble_view, stackView, false /* attachToRoot */); - info.imageView.initialize(controller.getPositioner()); + info.imageView.initialize(positioner); - BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller); + BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory); info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); info.expandedView.initialize( - controller, stackView, false /* isOverflow */, bubbleTaskView); + expandedViewManager, stackView, positioner, false /* isOverflow */, + bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 73a9cf456a6e..ebb8e3e2d207 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -36,8 +36,9 @@ import android.widget.FrameLayout; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; -import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.bubbles.BubbleExpandedViewManager; import com.android.wm.shell.bubbles.BubbleOverflowContainerView; +import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleTaskView; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; import com.android.wm.shell.bubbles.Bubbles; @@ -45,11 +46,7 @@ import com.android.wm.shell.taskview.TaskView; import java.util.function.Supplier; -/** - * Expanded view of a bubble when it's part of the bubble bar. - * - * {@link BubbleController#isShowingAsBubbleBar()} - */ +/** Expanded view of a bubble when it's part of the bubble bar. */ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskViewHelper.Listener { /** * The expanded view listener notifying the {@link BubbleBarLayerView} about the internal @@ -67,7 +64,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private static final String TAG = BubbleBarExpandedView.class.getSimpleName(); private static final int INVALID_TASK_ID = -1; - private BubbleController mController; + private BubbleExpandedViewManager mManager; private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; @@ -133,20 +130,22 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mMenuViewController.hideMenu(false /* animated */); } - /** Set the BubbleController on the view, must be called before doing anything else. */ - public void initialize(BubbleController controller, boolean isOverflow, + /** Initializes the view, must be called before doing anything else. */ + public void initialize(BubbleExpandedViewManager expandedViewManager, + BubblePositioner positioner, + boolean isOverflow, @Nullable BubbleTaskView bubbleTaskView) { - mController = controller; + mManager = expandedViewManager; mIsOverflow = isOverflow; if (mIsOverflow) { mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate( R.layout.bubble_overflow_container, null /* root */); - mOverflowView.setBubbleController(mController); + mOverflowView.initialize(expandedViewManager, positioner); addView(mOverflowView); } else { mTaskView = bubbleTaskView.getTaskView(); - mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController, + mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager, /* listener= */ this, bubbleTaskView, /* viewParent= */ this); if (mTaskView.getParent() != null) { @@ -178,13 +177,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onOpenAppSettings(Bubble bubble) { - mController.collapseStack(); + mManager.collapseStack(); mContext.startActivityAsUser(bubble.getSettingsIntent(mContext), bubble.getUser()); } @Override public void onDismissBubble(Bubble bubble) { - mController.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); + mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); } }); mHandleView.setOnClickListener(view -> { @@ -279,7 +278,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView if (mMenuViewController.isMenuVisible()) { mMenuViewController.hideMenu(/* animated = */ true); } else { - mController.collapseStack(); + mManager.collapseStack(); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java index f5b0174642d1..094af9652ea3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java @@ -18,7 +18,6 @@ package com.android.wm.shell.bubbles; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; @@ -45,18 +44,22 @@ public class BubbleOverflowTest extends ShellTestCase { private TestableBubblePositioner mPositioner; private BubbleOverflow mOverflow; + private BubbleExpandedViewManager mExpandedViewManager; @Mock private BubbleController mBubbleController; + @Mock + private BubbleStackView mBubbleStackView; @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); + mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController); mPositioner = new TestableBubblePositioner(mContext, mContext.getSystemService(WindowManager.class)); when(mBubbleController.getPositioner()).thenReturn(mPositioner); - when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class)); + when(mBubbleController.getStackView()).thenReturn(mBubbleStackView); mOverflow = new BubbleOverflow(mContext, mPositioner); } @@ -65,7 +68,7 @@ public class BubbleOverflowTest extends ShellTestCase { public void test_initialize_forStack() { assertThat(mOverflow.getExpandedView()).isNull(); - mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false); + mOverflow.initialize(mExpandedViewManager, mBubbleStackView, mPositioner); assertThat(mOverflow.getExpandedView()).isNotNull(); assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY); @@ -74,7 +77,7 @@ public class BubbleOverflowTest extends ShellTestCase { @Test public void test_initialize_forBubbleBar() { - mOverflow.initialize(mBubbleController, /* forBubbleBar= */ true); + mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner); assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull(); assertThat(mOverflow.getExpandedView()).isNull(); @@ -82,11 +85,10 @@ public class BubbleOverflowTest extends ShellTestCase { @Test public void test_cleanUpExpandedState() { - mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false); + mOverflow.initialize(mExpandedViewManager, mBubbleStackView, mPositioner); assertThat(mOverflow.getExpandedView()).isNotNull(); mOverflow.cleanUpExpandedState(); assertThat(mOverflow.getExpandedView()).isNull(); } - } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt index 1668e3712462..ae39fbcb4eed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -44,6 +44,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.taskview.TaskView import com.android.wm.shell.taskview.TaskViewTransitions import com.android.wm.shell.transition.Transitions import com.google.common.truth.Truth.assertThat @@ -55,6 +56,7 @@ import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever +import java.util.concurrent.Executor /** Tests for loading / inflating views & icons for a bubble. */ @SmallTest @@ -65,11 +67,16 @@ class BubbleViewInfoTest : ShellTestCase() { private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener private lateinit var iconFactory: BubbleIconFactory private lateinit var bubble: Bubble - private lateinit var bubbleController: BubbleController private lateinit var mainExecutor: ShellExecutor private lateinit var bubbleStackView: BubbleStackView private lateinit var bubbleBarLayerView: BubbleBarLayerView + private lateinit var bubblePositioner: BubblePositioner + private lateinit var expandedViewManager: BubbleExpandedViewManager + + private val bubbleTaskViewFactory = BubbleTaskViewFactory { + BubbleTaskView(mock<TaskView>(), mock<Executor>()) + } @Before fun setup() { @@ -88,7 +95,7 @@ class BubbleViewInfoTest : ShellTestCase() { val shellInit = ShellInit(mainExecutor) val shellCommandHandler = ShellCommandHandler() val shellController = ShellController(context, shellInit, shellCommandHandler, mainExecutor) - val bubblePositioner = BubblePositioner(context, windowManager) + bubblePositioner = BubblePositioner(context, windowManager) val bubbleData = BubbleData( context, @@ -143,6 +150,7 @@ class BubbleViewInfoTest : ShellTestCase() { bubbleController, mainExecutor ) + expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController) bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData) } @@ -152,7 +160,9 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populate( context, - bubbleController, + expandedViewManager, + bubbleTaskViewFactory, + bubblePositioner, bubbleStackView, iconFactory, bubble, @@ -178,7 +188,9 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar( context, - bubbleController, + expandedViewManager, + bubbleTaskViewFactory, + bubblePositioner, bubbleBarLayerView, iconFactory, bubble, @@ -212,7 +224,9 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar( context, - bubbleController, + expandedViewManager, + bubbleTaskViewFactory, + bubblePositioner, bubbleBarLayerView, iconFactory, bubble, diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp index cc79cba5e19c..5e73e7628568 100644 --- a/libs/hwui/apex/android_paint.cpp +++ b/libs/hwui/apex/android_paint.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "android/graphics/paint.h" +#include <SkBlendMode.h> +#include <SkImageFilter.h> +#include <hwui/Paint.h> #include "TypeCast.h" - -#include <hwui/Paint.h> -#include <SkBlendMode.h> +#include "android/graphics/paint.h" +#include "include/effects/SkImageFilters.h" using namespace android; @@ -43,6 +44,22 @@ static SkBlendMode convertBlendMode(ABlendMode blendMode) { } } +static sk_sp<SkImageFilter> convertImageFilter(AImageFilter imageFilter) { + switch (imageFilter) { + case AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON: + // Material Elevation Level 1 Drop Shadow. + sk_sp<SkImageFilter> key_shadow = SkImageFilters::DropShadow( + 0.0f, 1.0f, 2.0f, 2.0f, SkColorSetARGB(0x4D, 0x00, 0x00, 0x00), nullptr); + sk_sp<SkImageFilter> ambient_shadow = SkImageFilters::DropShadow( + 0.0f, 1.0f, 3.0f, 3.0f, SkColorSetARGB(0x26, 0x00, 0x00, 0x00), nullptr); + return SkImageFilters::Compose(ambient_shadow, key_shadow); + } +} + void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) { TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode)); } + +void APaint_setImageFilter(APaint* paint, AImageFilter imageFilter) { + TypeCast::toPaint(paint)->setImageFilter(convertImageFilter(imageFilter)); +} diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h index 058db8d37619..36b7575d585d 100644 --- a/libs/hwui/apex/include/android/graphics/paint.h +++ b/libs/hwui/apex/include/android/graphics/paint.h @@ -26,6 +26,14 @@ __BEGIN_DECLS */ typedef struct APaint APaint; +/** + * Predefined Image filter type. + */ +enum AImageFilter { + /** Drop shadow image filter for PointerIcons. */ + AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON = 0, +}; + /** Bitmap pixel format. */ enum ABlendMode { /** replaces destination with zero: fully transparent */ @@ -42,6 +50,8 @@ ANDROID_API void APaint_destroyPaint(APaint* paint); ANDROID_API void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); +ANDROID_API void APaint_setImageFilter(APaint* paint, AImageFilter imageFilter); + __END_DECLS #ifdef __cplusplus @@ -54,6 +64,10 @@ namespace graphics { void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); } + void setImageFilter(AImageFilter imageFilter) { + APaint_setImageFilter(mPaint, imageFilter); + } + const APaint& get() const { return *mPaint; } private: diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt index fdb237387098..d03ceb471d6c 100644 --- a/libs/hwui/libhwui.map.txt +++ b/libs/hwui/libhwui.map.txt @@ -32,6 +32,7 @@ LIBHWUI { # platform-only /* HWUI isn't current a module, so all of these are st APaint_createPaint; APaint_destroyPaint; APaint_setBlendMode; + APaint_setImageFilter; ARegionIterator_acquireIterator; ARegionIterator_releaseIterator; ARegionIterator_isComplex; diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp index b7e51e22a214..59e36e4b0d1e 100644 --- a/libs/input/SpriteIcon.cpp +++ b/libs/input/SpriteIcon.cpp @@ -34,6 +34,9 @@ bool SpriteIcon::draw(sp<Surface> surface) const { graphics::Paint paint; paint.setBlendMode(ABLEND_MODE_SRC); + if (drawNativeDropShadow) { + paint.setImageFilter(AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON); + } graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace()); canvas.drawBitmap(bitmap, 0, 0, &paint); diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h index 5f085bbd2374..9e6cc81f0bb4 100644 --- a/libs/input/SpriteIcon.h +++ b/libs/input/SpriteIcon.h @@ -29,16 +29,22 @@ namespace android { struct SpriteIcon { inline SpriteIcon() : style(PointerIconStyle::TYPE_NULL), hotSpotX(0), hotSpotY(0) {} inline SpriteIcon(const graphics::Bitmap& bitmap, PointerIconStyle style, float hotSpotX, - float hotSpotY) - : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {} + float hotSpotY, bool drawNativeDropShadow) + : bitmap(bitmap), + style(style), + hotSpotX(hotSpotX), + hotSpotY(hotSpotY), + drawNativeDropShadow(drawNativeDropShadow) {} graphics::Bitmap bitmap; PointerIconStyle style; float hotSpotX; float hotSpotY; + bool drawNativeDropShadow; inline SpriteIcon copy() const { - return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); + return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY, + drawNativeDropShadow); } inline void reset() { @@ -46,6 +52,7 @@ struct SpriteIcon { style = PointerIconStyle::TYPE_NULL; hotSpotX = 0; hotSpotY = 0; + drawNativeDropShadow = false; } inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); } diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java index 25b14041c8fa..4cdfc515fe9a 100644 --- a/media/java/android/media/AudioHalVersionInfo.java +++ b/media/java/android/media/AudioHalVersionInfo.java @@ -81,7 +81,7 @@ public final class AudioHalVersionInfo implements Parcelable, Comparable<AudioHa * defined in frameworks/av/media/libaudiohal/FactoryHal.cpp. */ public static final @NonNull List<AudioHalVersionInfo> VERSIONS = - List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0); + List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0); private static final String TAG = "AudioHalVersionInfo"; private AudioHalVersion mHalVersion = new AudioHalVersion(); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e9ba779c1b13..8f3f82e06cf8 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5513,6 +5513,26 @@ public class AudioManager { /** * @hide + * @return All currently registered audio policy mixes. + */ + @TestApi + @FlaggedApi(android.media.audiopolicy.Flags.FLAG_AUDIO_MIX_TEST_API) + @NonNull + public List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes() { + if (!android.media.audiopolicy.Flags.audioMixTestApi()) { + return Collections.emptyList(); + } + + final IAudioService service = getService(); + try { + return service.getRegisteredPolicyMixes(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * @return true if an AudioPolicy was previously registered */ @TestApi diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index f73be35fa130..293c561f166c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1984,6 +1984,9 @@ public class AudioSystem public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register); /** @hide */ + public static native int getRegisteredPolicyMixes(@NonNull List<AudioMix> devices); + + /** @hide */ public static native int updatePolicyMixes( AudioMix[] mixes, AudioMixingRule[] updatedMixingRules); diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java index 40b0e3e03ef6..4f1a8ee5e728 100644 --- a/media/java/android/media/FadeManagerConfiguration.java +++ b/media/java/android/media/FadeManagerConfiguration.java @@ -18,6 +18,7 @@ package android.media; import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION; +import android.annotation.DurationMillisLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -112,17 +113,11 @@ public final class FadeManagerConfiguration implements Parcelable { */ public static final int FADE_STATE_ENABLED_DEFAULT = 1; - /** - * Defines the enabled state with Automotive specific configurations - */ - public static final int FADE_STATE_ENABLED_AUTO = 2; - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = false, prefix = "FADE_STATE", value = { FADE_STATE_DISABLED, FADE_STATE_ENABLED_DEFAULT, - FADE_STATE_ENABLED_AUTO, }) public @interface FadeStateEnum {} @@ -143,7 +138,14 @@ public final class FadeManagerConfiguration implements Parcelable { * @see #getFadeOutDurationForAudioAttributes(AudioAttributes) * @see #getFadeInDurationForAudioAttributes(AudioAttributes) */ - public static final long DURATION_NOT_SET = 0; + public static final @DurationMillisLong long DURATION_NOT_SET = 0; + + /** Defines the default fade out duration */ + private static final @DurationMillisLong long DEFAULT_FADE_OUT_DURATION_MS = 2_000; + + /** Defines the default fade in duration */ + private static final @DurationMillisLong long DEFAULT_FADE_IN_DURATION_MS = 1_000; + /** Map of Usage to Fade volume shaper configs wrapper */ private final SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap; /** Map of AudioAttributes to Fade volume shaper configs wrapper */ @@ -161,14 +163,15 @@ public final class FadeManagerConfiguration implements Parcelable { /** fade state */ private final @FadeStateEnum int mFadeState; /** fade out duration from builder - used for creating default fade out volume shaper */ - private final long mFadeOutDurationMillis; + private final @DurationMillisLong long mFadeOutDurationMillis; /** fade in duration from builder - used for creating default fade in volume shaper */ - private final long mFadeInDurationMillis; + private final @DurationMillisLong long mFadeInDurationMillis; /** delay after which the offending players are faded back in */ - private final long mFadeInDelayForOffendersMillis; + private final @DurationMillisLong long mFadeInDelayForOffendersMillis; - private FadeManagerConfiguration(int fadeState, long fadeOutDurationMillis, - long fadeInDurationMillis, long offendersFadeInDelayMillis, + private FadeManagerConfiguration(int fadeState, @DurationMillisLong long fadeOutDurationMillis, + @DurationMillisLong long fadeInDurationMillis, + @DurationMillisLong long offendersFadeInDelayMillis, @NonNull SparseArray<FadeVolumeShaperConfigsWrapper> usageToFadeWrapperMap, @NonNull ArrayMap<AudioAttributes, FadeVolumeShaperConfigsWrapper> attrToFadeWrapperMap, @NonNull IntArray fadeableUsages, @NonNull IntArray unfadeableContentTypes, @@ -196,8 +199,6 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Get the fade state - * - * @return one of the {@link FadeStateEnum} state */ @FadeStateEnum public int getFadeState() { @@ -207,7 +208,7 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Get the list of usages that can be faded * - * @return list of {@link android.media.AudioAttributes.AttributeUsage} that shall be faded + * @return list of {@link android.media.AudioAttributes usages} that shall be faded * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @NonNull @@ -217,10 +218,10 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Get the list of {@link android.media.AudioPlaybackConfiguration.PlayerType player types} - * that cannot be faded + * Get the list of {@link android.media.AudioPlaybackConfiguration player types} that can be + * faded * - * @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType} + * @return list of {@link android.media.AudioPlaybackConfiguration player types} * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @NonNull @@ -230,10 +231,9 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Get the list of {@link android.media.AudioAttributes.AttributeContentType content types} - * that cannot be faded + * Get the list of {@link android.media.AudioAttributes content types} that can be faded * - * @return list of {@link android.media.AudioAttributes.AttributeContentType} + * @return list of {@link android.media.AudioAttributes content types} * @throws IllegalStateExceptionif if the fade state is set to {@link #FADE_STATE_DISABLED} */ @NonNull @@ -267,15 +267,15 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Get the duration used to fade out players with - * {@link android.media.AudioAttributes.AttributeUsage} + * Get the duration used to fade out players with {@link android.media.AudioAttributes usage} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} + * @param usage the {@link android.media.AudioAttributes usage} * @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise * @throws IllegalArgumentException if the usage is invalid * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ - public long getFadeOutDurationForUsage(int usage) { + @DurationMillisLong + public long getFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage) { ensureFadingIsEnabled(); validateUsage(usage); return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper( @@ -283,15 +283,15 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Get the duration used to fade in players with - * {@link android.media.AudioAttributes.AttributeUsage} + * Get the duration used to fade in players with {@link android.media.AudioAttributes usage} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} + * @param usage the {@link android.media.AudioAttributes usage} * @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise * @throws IllegalArgumentException if the usage is invalid * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ - public long getFadeInDurationForUsage(int usage) { + @DurationMillisLong + public long getFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage) { ensureFadingIsEnabled(); validateUsage(usage); return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper( @@ -300,16 +300,17 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Get the {@link android.media.VolumeShaper.Configuration} used to fade out players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} + * @param usage the {@link android.media.AudioAttributes usage} * @return {@link android.media.VolumeShaper.Configuration} if set for the usage or - * {@code null} otherwise + * {@code null} otherwise * @throws IllegalArgumentException if the usage is invalid * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @Nullable - public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int usage) { + public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage( + @AudioAttributes.AttributeUsage int usage) { ensureFadingIsEnabled(); validateUsage(usage); return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage), @@ -318,16 +319,17 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Get the {@link android.media.VolumeShaper.Configuration} used to fade in players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of player + * @param usage the {@link android.media.AudioAttributes usage} * @return {@link android.media.VolumeShaper.Configuration} if set for the usage or - * {@code null} otherwise + * {@code null} otherwise * @throws IllegalArgumentException if the usage is invalid * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @Nullable - public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int usage) { + public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage( + @AudioAttributes.AttributeUsage int usage) { ensureFadingIsEnabled(); validateUsage(usage); return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage), @@ -339,10 +341,11 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes {@link android.media.AudioAttributes} * @return duration in milliseconds if set for the audio attributes or - * {@link #DURATION_NOT_SET} otherwise + * {@link #DURATION_NOT_SET} otherwise * @throws NullPointerException if the audio attributes is {@code null} * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ + @DurationMillisLong public long getFadeOutDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) { ensureFadingIsEnabled(); return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper( @@ -354,10 +357,11 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes {@link android.media.AudioAttributes} * @return duration in milliseconds if set for the audio attributes or - * {@link #DURATION_NOT_SET} otherwise + * {@link #DURATION_NOT_SET} otherwise * @throws NullPointerException if the audio attributes is {@code null} * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ + @DurationMillisLong public long getFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) { ensureFadingIsEnabled(); return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper( @@ -370,7 +374,7 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes {@link android.media.AudioAttributes} * @return {@link android.media.VolumeShaper.Configuration} if set for the audio attribute or - * {@code null} otherwise + * {@code null} otherwise * @throws NullPointerException if the audio attributes is {@code null} * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @@ -389,7 +393,7 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes {@link android.media.AudioAttributes} * @return {@link android.media.VolumeShaper.Configuration} used for fading in if set for the - * audio attribute or {@code null} otherwise + * audio attribute or {@code null} otherwise * @throws NullPointerException if the audio attributes is {@code null} * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED} */ @@ -407,7 +411,7 @@ public final class FadeManagerConfiguration implements Parcelable { * configurations are defined * * @return list of {@link android.media.AudioAttributes} with valid volume shaper configs or - * empty list if none set. + * empty list if none set. */ @NonNull public List<AudioAttributes> getAudioAttributesWithVolumeShaperConfigs() { @@ -417,8 +421,14 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Get the delay after which the offending players are faded back in * + * Players are categorized as offending if they do not honor audio focus state changes. For + * example - when an app loses audio focus, it is expected that the app stops any active + * player in favor of the app(s) that gained audio focus. However, if the app do not stop the + * audio playback, such players are termed as offenders. + * * @return delay in milliseconds */ + @DurationMillisLong public long getFadeInDelayForOffenders() { return mFadeInDelayForOffendersMillis; } @@ -435,8 +445,9 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Query if the usage is fadeable * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} - * @return {@code true} if usage is fadeable, {@code false} otherwise + * @param usage the {@link android.media.AudioAttributes usage} + * @return {@code true} if usage is fadeable, {@code false} when the fade state is set to + * {@link #FADE_STATE_DISABLED} or if the usage is not fadeable. */ public boolean isUsageFadeable(@AudioAttributes.AttributeUsage int usage) { if (!isFadeEnabled()) { @@ -448,9 +459,9 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Query if the content type is unfadeable * - * @param contentType the {@link android.media.AudioAttributes.AttributeContentType} + * @param contentType the {@link android.media.AudioAttributes content type} * @return {@code true} if content type is unfadeable or if fade state is set to - * {@link #FADE_STATE_DISABLED}, {@code false} otherwise + * {@link #FADE_STATE_DISABLED}, {@code false} otherwise */ public boolean isContentTypeUnfadeable(@AudioAttributes.AttributeContentType int contentType) { if (!isFadeEnabled()) { @@ -462,11 +473,11 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Query if the player type is unfadeable * - * @param playerType the {@link android.media.AudioPlaybackConfiguration} player type + * @param playerType the {@link android.media.AudioPlaybackConfiguration player type} * @return {@code true} if player type is unfadeable or if fade state is set to - * {@link #FADE_STATE_DISABLED}, {@code false} otherwise + * {@link #FADE_STATE_DISABLED}, {@code false} otherwise */ - public boolean isPlayerTypeUnfadeable(int playerType) { + public boolean isPlayerTypeUnfadeable(@AudioPlaybackConfiguration.PlayerType int playerType) { if (!isFadeEnabled()) { return true; } @@ -478,7 +489,7 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes the {@link android.media.AudioAttributes} * @return {@code true} if audio attributes is unfadeable or if fade state is set to - * {@link #FADE_STATE_DISABLED}, {@code false} otherwise + * {@link #FADE_STATE_DISABLED}, {@code false} otherwise * @throws NullPointerException if the audio attributes is {@code null} */ public boolean isAudioAttributesUnfadeable(@NonNull AudioAttributes audioAttributes) { @@ -494,7 +505,7 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param uid the uid of application * @return {@code true} if uid is unfadeable or if fade state is set to - * {@link #FADE_STATE_DISABLED}, {@code false} otherwise + * {@link #FADE_STATE_DISABLED}, {@code false} otherwise */ public boolean isUidUnfadeable(int uid) { if (!isFadeEnabled()) { @@ -503,6 +514,20 @@ public final class FadeManagerConfiguration implements Parcelable { return mUnfadeableUids.contains(uid); } + /** + * Returns the default fade out duration (in milliseconds) + */ + public static @DurationMillisLong long getDefaultFadeOutDurationMillis() { + return DEFAULT_FADE_OUT_DURATION_MS; + } + + /** + * Returns the default fade in duration (in milliseconds) + */ + public static @DurationMillisLong long getDefaultFadeInDurationMillis() { + return DEFAULT_FADE_IN_DURATION_MS; + } + @Override public String toString() { return "FadeManagerConfiguration { fade state = " + fadeStateToString(mFadeState) @@ -520,7 +545,7 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Convert fade state into a human-readable string * - * @param fadeState one of the fade state in {@link FadeStateEnum} + * @param fadeState one of {@link #FADE_STATE_DISABLED} or {@link #FADE_STATE_ENABLED_DEFAULT} * @return human-readable string * @hide */ @@ -531,8 +556,6 @@ public final class FadeManagerConfiguration implements Parcelable { return "FADE_STATE_DISABLED"; case FADE_STATE_ENABLED_DEFAULT: return "FADE_STATE_ENABLED_DEFAULT"; - case FADE_STATE_ENABLED_AUTO: - return "FADE_STATE_ENABLED_AUTO"; default: return "unknown fade state: " + fadeState; } @@ -712,9 +735,9 @@ public final class FadeManagerConfiguration implements Parcelable { * * <p><b>Notes:</b> * <ul> - * <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT} or - * {@link #FADE_STATE_ENABLED_AUTO}, the builder expects at least one valid usage to be - * set/added. Failure to do so will result in an exception during {@link #build()}</li> + * <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT}, the builder expects at + * least one valid usage to be set/added. Failure to do so will result in an exception + * during {@link #build()}</li> * <li>Every usage added to the fadeable list should have corresponding volume shaper * configs defined. This can be achieved by setting either the duration or volume shaper * config through {@link #setFadeOutDurationForUsage(int, long)} or @@ -741,11 +764,6 @@ public final class FadeManagerConfiguration implements Parcelable { private static final long IS_FADEABLE_USAGES_FIELD_SET = 1 << 1; private static final long IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET = 1 << 2; - /** duration of the fade out curve */ - private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000; - /** duration of the fade in curve */ - private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000; - /** * delay after which a faded out player will be faded back in. This will be heard by the * user only in the case of unmuting players that didn't respect audio focus and didn't @@ -771,9 +789,10 @@ public final class FadeManagerConfiguration implements Parcelable { }); private int mFadeState = FADE_STATE_ENABLED_DEFAULT; - private long mFadeInDelayForOffendersMillis = DEFAULT_DELAY_FADE_IN_OFFENDERS_MS; - private long mFadeOutDurationMillis; - private long mFadeInDurationMillis; + private @DurationMillisLong long mFadeInDelayForOffendersMillis = + DEFAULT_DELAY_FADE_IN_OFFENDERS_MS; + private @DurationMillisLong long mFadeOutDurationMillis; + private @DurationMillisLong long mFadeInDurationMillis; private long mBuilderFieldsSet; private SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap = new SparseArray<>(); @@ -787,7 +806,8 @@ public final class FadeManagerConfiguration implements Parcelable { private List<AudioAttributes> mUnfadeableAudioAttributes = new ArrayList<>(); /** - * Constructs a new Builder with default fade out and fade in durations + * Constructs a new Builder with {@link #DEFAULT_FADE_OUT_DURATION_MS} and + * {@link #DEFAULT_FADE_IN_DURATION_MS} durations. */ public Builder() { mFadeOutDurationMillis = DEFAULT_FADE_OUT_DURATION_MS; @@ -800,7 +820,8 @@ public final class FadeManagerConfiguration implements Parcelable { * @param fadeOutDurationMillis duration in milliseconds used for fading out * @param fadeInDurationMills duration in milliseconds used for fading in */ - public Builder(long fadeOutDurationMillis, long fadeInDurationMills) { + public Builder(@DurationMillisLong long fadeOutDurationMillis, + @DurationMillisLong long fadeInDurationMills) { mFadeOutDurationMillis = fadeOutDurationMillis; mFadeInDurationMillis = fadeInDurationMills; } @@ -830,7 +851,8 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Set the overall fade state * - * @param state one of the {@link FadeStateEnum} states + * @param state one of the {@link #FADE_STATE_DISABLED} or + * {@link #FADE_STATE_ENABLED_DEFAULT} states * @return the same Builder instance * @throws IllegalArgumentException if the fade state is invalid * @see #getFadeState() @@ -844,21 +866,22 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Set the {@link android.media.VolumeShaper.Configuration} used to fade out players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * <p> * This method accepts {@code null} for volume shaper config to clear a previously set * configuration (example, if set through * {@link #Builder(android.media.FadeManagerConfiguration)}) * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player + * @param usage the {@link android.media.AudioAttributes usage} of target player * @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used - * to fade out players with usage + * to fade out players with usage * @return the same Builder instance * @throws IllegalArgumentException if the usage is invalid * @see #getFadeOutVolumeShaperConfigForUsage(int) */ @NonNull - public Builder setFadeOutVolumeShaperConfigForUsage(int usage, + public Builder setFadeOutVolumeShaperConfigForUsage( + @AudioAttributes.AttributeUsage int usage, @Nullable VolumeShaper.Configuration fadeOutVShaperConfig) { validateUsage(usage); getFadeVolShaperConfigWrapperForUsage(usage) @@ -869,21 +892,22 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Set the {@link android.media.VolumeShaper.Configuration} used to fade in players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * <p> * This method accepts {@code null} for volume shaper config to clear a previously set * configuration (example, if set through * {@link #Builder(android.media.FadeManagerConfiguration)}) * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} + * @param usage the {@link android.media.AudioAttributes usage} * @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used - * to fade in players with usage + * to fade in players with usage * @return the same Builder instance * @throws IllegalArgumentException if the usage is invalid * @see #getFadeInVolumeShaperConfigForUsage(int) */ @NonNull - public Builder setFadeInVolumeShaperConfigForUsage(int usage, + public Builder setFadeInVolumeShaperConfigForUsage( + @AudioAttributes.AttributeUsage int usage, @Nullable VolumeShaper.Configuration fadeInVShaperConfig) { validateUsage(usage); getFadeVolShaperConfigWrapperForUsage(usage) @@ -894,7 +918,7 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Set the duration used for fading out players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * <p> * A Volume shaper configuration is generated with the provided duration and default * volume curve definitions. This config is then used to fade out players with given usage. @@ -904,17 +928,18 @@ public final class FadeManagerConfiguration implements Parcelable { * {@link #DURATION_NOT_SET} and sets the corresponding fade out volume shaper config to * {@code null} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player + * @param usage the {@link android.media.AudioAttributes usage} of target player * @param fadeOutDurationMillis positive duration in milliseconds or - * {@link #DURATION_NOT_SET} + * {@link #DURATION_NOT_SET} * @return the same Builder instance * @throws IllegalArgumentException if the fade out duration is non-positive with the - * exception of {@link #DURATION_NOT_SET} + * exception of {@link #DURATION_NOT_SET} * @see #setFadeOutVolumeShaperConfigForUsage(int, VolumeShaper.Configuration) * @see #getFadeOutDurationForUsage(int) */ @NonNull - public Builder setFadeOutDurationForUsage(int usage, long fadeOutDurationMillis) { + public Builder setFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage, + @DurationMillisLong long fadeOutDurationMillis) { validateUsage(usage); VolumeShaper.Configuration fadeOutVShaperConfig = createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false); @@ -924,7 +949,7 @@ public final class FadeManagerConfiguration implements Parcelable { /** * Set the duration used for fading in players with - * {@link android.media.AudioAttributes.AttributeUsage} + * {@link android.media.AudioAttributes usage} * <p> * A Volume shaper configuration is generated with the provided duration and default * volume curve definitions. This config is then used to fade in players with given usage. @@ -934,17 +959,18 @@ public final class FadeManagerConfiguration implements Parcelable { * {@link #DURATION_NOT_SET} and sets the corresponding fade in volume shaper config to * {@code null} * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player + * @param usage the {@link android.media.AudioAttributes usage} of target player * @param fadeInDurationMillis positive duration in milliseconds or - * {@link #DURATION_NOT_SET} + * {@link #DURATION_NOT_SET} * @return the same Builder instance * @throws IllegalArgumentException if the fade in duration is non-positive with the - * exception of {@link #DURATION_NOT_SET} + * exception of {@link #DURATION_NOT_SET} * @see #setFadeInVolumeShaperConfigForUsage(int, VolumeShaper.Configuration) * @see #getFadeInDurationForUsage(int) */ @NonNull - public Builder setFadeInDurationForUsage(int usage, long fadeInDurationMillis) { + public Builder setFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage, + @DurationMillisLong long fadeInDurationMillis) { validateUsage(usage); VolumeShaper.Configuration fadeInVShaperConfig = createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true); @@ -962,9 +988,8 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes the {@link android.media.AudioAttributes} * @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to - * fade out players with audio attribute + * fade out players with audio attribute * @return the same Builder instance - * @throws NullPointerException if the audio attributes is {@code null} * @see #getFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes) */ @NonNull @@ -988,7 +1013,7 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes the {@link android.media.AudioAttributes} * @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to - * fade in players with audio attribute + * fade in players with audio attribute * @return the same Builder instance * @throws NullPointerException if the audio attributes is {@code null} * @see #getFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes) @@ -1017,12 +1042,12 @@ public final class FadeManagerConfiguration implements Parcelable { * {@code null} * * @param audioAttributes the {@link android.media.AudioAttributes} for which the fade out - * duration will be set/updated/reset + * duration will be set/updated/reset * @param fadeOutDurationMillis positive duration in milliseconds or - * {@link #DURATION_NOT_SET} + * {@link #DURATION_NOT_SET} * @return the same Builder instance * @throws IllegalArgumentException if the fade out duration is non-positive with the - * exception of {@link #DURATION_NOT_SET} + * exception of {@link #DURATION_NOT_SET} * @see #getFadeOutDurationForAudioAttributes(AudioAttributes) * @see #setFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes, * VolumeShaper.Configuration) @@ -1030,7 +1055,7 @@ public final class FadeManagerConfiguration implements Parcelable { @NonNull public Builder setFadeOutDurationForAudioAttributes( @NonNull AudioAttributes audioAttributes, - long fadeOutDurationMillis) { + @DurationMillisLong long fadeOutDurationMillis) { Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null"); VolumeShaper.Configuration fadeOutVShaperConfig = createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false); @@ -1039,8 +1064,7 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Set the duration used for fading in players of type - * {@link android.media.AudioAttributes}. + * Set the duration used for fading in players of type {@link android.media.AudioAttributes} * <p> * A Volume shaper configuration is generated with the provided duration and default * volume curve definitions. This config is then used to fade in players with given usage. @@ -1051,19 +1075,19 @@ public final class FadeManagerConfiguration implements Parcelable { * {@code null} * * @param audioAttributes the {@link android.media.AudioAttributes} for which the fade in - * duration will be set/updated/reset + * duration will be set/updated/reset * @param fadeInDurationMillis positive duration in milliseconds or - * {@link #DURATION_NOT_SET} + * {@link #DURATION_NOT_SET} * @return the same Builder instance * @throws IllegalArgumentException if the fade in duration is non-positive with the - * exception of {@link #DURATION_NOT_SET} + * exception of {@link #DURATION_NOT_SET} * @see #getFadeInDurationForAudioAttributes(AudioAttributes) * @see #setFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes, * VolumeShaper.Configuration) */ @NonNull public Builder setFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes, - long fadeInDurationMillis) { + @DurationMillisLong long fadeInDurationMillis) { Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null"); VolumeShaper.Configuration fadeInVShaperConfig = createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true); @@ -1072,22 +1096,18 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Set the list of {@link android.media.AudioAttributes.AttributeUsage} that can be faded + * Set the list of {@link android.media.AudioAttributes usage} that can be faded * * <p>This is a positive list. Players with matching usage will be considered for fading. * Usages that are not part of this list will not be faded * - * <p>Passing an empty list as input clears the existing list. This can be used to - * reset the list when using a copy constructor - * * <p><b>Warning:</b> When fade state is set to enabled, the builder expects at least one * usage to be set/added. Failure to do so will result in an exception during * {@link #build()} * - * @param usages List of the {@link android.media.AudioAttributes.AttributeUsage} + * @param usages List of the {@link android.media.AudioAttributes usages} * @return the same Builder instance * @throws IllegalArgumentException if the usages are invalid - * @throws NullPointerException if the usage list is {@code null} * @see #getFadeableUsages() */ @NonNull @@ -1101,9 +1121,9 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Add the {@link android.media.AudioAttributes.AttributeUsage} to the fadeable list + * Add the {@link android.media.AudioAttributes usage} to the fadeable list * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} + * @param usage the {@link android.media.AudioAttributes usage} * @return the same Builder instance * @throws IllegalArgumentException if the usage is invalid * @see #getFadeableUsages() @@ -1120,30 +1140,23 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Remove the {@link android.media.AudioAttributes.AttributeUsage} from the fadeable list - * <p> - * Players of this usage type will not be faded. + * Clears the fadeable {@link android.media.AudioAttributes usage} list + * + * <p>This can be used to reset the list when using a copy constructor * - * @param usage the {@link android.media.AudioAttributes.AttributeUsage} * @return the same Builder instance - * @throws IllegalArgumentException if the usage is invalid * @see #getFadeableUsages() * @see #setFadeableUsages(List) */ @NonNull - public Builder clearFadeableUsage(@AudioAttributes.AttributeUsage int usage) { - validateUsage(usage); + public Builder clearFadeableUsages() { setFlag(IS_FADEABLE_USAGES_FIELD_SET); - int index = mFadeableUsages.indexOf(usage); - if (index != INVALID_INDEX) { - mFadeableUsages.remove(index); - } + mFadeableUsages.clear(); return this; } /** - * Set the list of {@link android.media.AudioAttributes.AttributeContentType} that can not - * be faded + * Set the list of {@link android.media.AudioAttributes content type} that can not be faded * * <p>This is a negative list. Players with matching content type of this list will not be * faded. Content types that are not part of this list will be considered for fading. @@ -1151,10 +1164,9 @@ public final class FadeManagerConfiguration implements Parcelable { * <p>Passing an empty list as input clears the existing list. This can be used to * reset the list when using a copy constructor * - * @param contentTypes list of {@link android.media.AudioAttributes.AttributeContentType} + * @param contentTypes list of {@link android.media.AudioAttributes content types} * @return the same Builder instance * @throws IllegalArgumentException if the content types are invalid - * @throws NullPointerException if the content type list is {@code null} * @see #getUnfadeableContentTypes() */ @NonNull @@ -1168,9 +1180,9 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Add the {@link android.media.AudioAttributes.AttributeContentType} to unfadeable list + * Add the {@link android.media.AudioAttributes content type} to unfadeable list * - * @param contentType the {@link android.media.AudioAttributes.AttributeContentType} + * @param contentType the {@link android.media.AudioAttributes content type} * @return the same Builder instance * @throws IllegalArgumentException if the content type is invalid * @see #setUnfadeableContentTypes(List) @@ -1188,24 +1200,18 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Remove the {@link android.media.AudioAttributes.AttributeContentType} from the - * unfadeable list + * Clears the unfadeable {@link android.media.AudioAttributes content type} list + * + * <p>This can be used to reset the list when using a copy constructor * - * @param contentType the {@link android.media.AudioAttributes.AttributeContentType} * @return the same Builder instance - * @throws IllegalArgumentException if the content type is invalid * @see #setUnfadeableContentTypes(List) * @see #getUnfadeableContentTypes() */ @NonNull - public Builder clearUnfadeableContentType( - @AudioAttributes.AttributeContentType int contentType) { - validateContentType(contentType); + public Builder clearUnfadeableContentTypes() { setFlag(IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET); - int index = mUnfadeableContentTypes.indexOf(contentType); - if (index != INVALID_INDEX) { - mUnfadeableContentTypes.remove(index); - } + mUnfadeableContentTypes.clear(); return this; } @@ -1213,14 +1219,10 @@ public final class FadeManagerConfiguration implements Parcelable { * Set the uids that cannot be faded * * <p>This is a negative list. Players with matching uid of this list will not be faded. - * Uids that are not part of this list shall be considered for fading - * - * <p>Passing an empty list as input clears the existing list. This can be used to - * reset the list when using a copy constructor + * Uids that are not part of this list shall be considered for fading. * * @param uids list of uids * @return the same Builder instance - * @throws NullPointerException if the uid list is {@code null} * @see #getUnfadeableUids() */ @NonNull @@ -1248,19 +1250,17 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Remove the uid from unfadeable list + * Clears the unfadeable uid list + * + * <p>This can be used to reset the list when using a copy constructor. * - * @param uid client uid * @return the same Builder instance * @see #setUnfadeableUids(List) * @see #getUnfadeableUids() */ @NonNull - public Builder clearUnfadeableUid(int uid) { - int index = mUnfadeableUids.indexOf(uid); - if (index != INVALID_INDEX) { - mUnfadeableUids.remove(index); - } + public Builder clearUnfadeableUids() { + mUnfadeableUids.clear(); return this; } @@ -1270,24 +1270,19 @@ public final class FadeManagerConfiguration implements Parcelable { * <p>This is a negative list. Players with matching audio attributes of this list will not * be faded. Audio attributes that are not part of this list shall be considered for fading. * - * <p>Passing an empty list as input clears any existing list. This can be used to - * reset the list when using a copy constructor - * * <p><b>Note:</b> Be cautious when adding generic audio attributes into this list as it can - * negatively impact fadeability decision if such an audio attribute and corresponding - * usage fall into opposing lists. + * negatively impact fadeability decision (if such an audio attribute and corresponding + * usage fall into opposing lists). * For example: * <pre class=prettyprint> * AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build() </pre> * is a generic audio attribute for {@link android.media.AudioAttributes.USAGE_MEDIA}. - * It is an undefined behavior to have an - * {@link android.media.AudioAttributes.AttributeUsage} in the fadeable usage list and the - * corresponding generic {@link android.media.AudioAttributes} in the unfadeable list. Such - * cases will result in an exception during {@link #build()} + * It is an undefined behavior to have an {@link android.media.AudioAttributes usage} in the + * fadeable usage list and the corresponding generic {@link android.media.AudioAttributes} + * in the unfadeable list. Such cases will result in an exception during {@link #build()}. * * @param attrs list of {@link android.media.AudioAttributes} * @return the same Builder instance - * @throws NullPointerException if the audio attributes list is {@code null} * @see #getUnfadeableAudioAttributes() */ @NonNull @@ -1303,7 +1298,6 @@ public final class FadeManagerConfiguration implements Parcelable { * * @param audioAttributes the {@link android.media.AudioAttributes} * @return the same Builder instance - * @throws NullPointerException if the audio attributes is {@code null} * @see #setUnfadeableAudioAttributes(List) * @see #getUnfadeableAudioAttributes() */ @@ -1317,19 +1311,16 @@ public final class FadeManagerConfiguration implements Parcelable { } /** - * Remove the {@link android.media.AudioAttributes} from the unfadeable list. + * Clears the unfadeable {@link android.media.AudioAttributes} list. + * + * <p>This can be used to reset the list when using a copy constructor. * - * @param audioAttributes the {@link android.media.AudioAttributes} * @return the same Builder instance - * @throws NullPointerException if the audio attributes is {@code null} * @see #getUnfadeableAudioAttributes() */ @NonNull - public Builder clearUnfadeableAudioAttributes(@NonNull AudioAttributes audioAttributes) { - Objects.requireNonNull(audioAttributes, "Audio attributes cannot be null"); - if (mUnfadeableAudioAttributes.contains(audioAttributes)) { - mUnfadeableAudioAttributes.remove(audioAttributes); - } + public Builder clearUnfadeableAudioAttributes() { + mUnfadeableAudioAttributes.clear(); return this; } @@ -1345,7 +1336,7 @@ public final class FadeManagerConfiguration implements Parcelable { * @see #getFadeInDelayForOffenders() */ @NonNull - public Builder setFadeInDelayForOffenders(long delayMillis) { + public Builder setFadeInDelayForOffenders(@DurationMillisLong long delayMillis) { Preconditions.checkArgument(delayMillis >= 0, "Delay cannot be negative"); mFadeInDelayForOffendersMillis = delayMillis; return this; @@ -1469,7 +1460,6 @@ public final class FadeManagerConfiguration implements Parcelable { switch(state) { case FADE_STATE_DISABLED: case FADE_STATE_ENABLED_DEFAULT: - case FADE_STATE_ENABLED_AUTO: break; default: throw new IllegalArgumentException("Unknown fade state: " + state); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 8dfa6be0fd11..98bd3caf3f7d 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -365,6 +365,8 @@ interface IAudioService { oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); + List<AudioMix> getRegisteredPolicyMixes(); + void unregisterAudioPolicy(in IAudioPolicyCallback pcb); int addMixForPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index b85decc74ff8..bbe461c95ed9 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -58,6 +58,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -597,6 +598,21 @@ public class AudioPolicy { setRegistration(null); } + /** + * @hide + */ + @TestApi + @NonNull + @FlaggedApi(Flags.FLAG_AUDIO_MIX_TEST_API) + public List<AudioMix> getMixes() { + if (!Flags.audioMixTestApi()) { + return Collections.emptyList(); + } + synchronized (mLock) { + return List.copyOf(mConfig.getMixes()); + } + } + public void setRegistration(String regId) { synchronized (mLock) { mRegistrationId = regId; diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index ba7ab9aa97ed..fa9afa872091 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -103,11 +103,9 @@ public abstract class MediaBrowserService extends Service { RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED }) private @interface ResultFlags { } - private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); - private ConnectionRecord mCurConnection; private final Handler mHandler = new Handler(); - private ServiceBinder mBinder; - MediaSession.Token mSession; + + private final ServiceState mServiceState = new ServiceState(); /** * All the info about a connection. @@ -139,7 +137,7 @@ public abstract class MediaBrowserService extends Service { service.mHandler.post(new Runnable() { @Override public void run() { - service.mConnections.remove(callbacks.asBinder()); + service.mServiceState.mConnections.remove(callbacks.asBinder()); } }); } @@ -238,17 +236,16 @@ public abstract class MediaBrowserService extends Service { @Override public void run() { final IBinder b = callbacks.asBinder(); - // Clear out the old subscriptions. We are getting new ones. - service.mConnections.remove(b); + service.mServiceState.mConnections.remove(b); // Temporarily sets a placeholder ConnectionRecord to make // getCurrentBrowserInfo() work in onGetRoot(). - service.mCurConnection = + service.mServiceState.mCurConnection = new ConnectionRecord( service, pkg, pid, uid, rootHints, callbacks, null); BrowserRoot root = service.onGetRoot(pkg, uid, rootHints); - service.mCurConnection = null; + service.mServiceState.mCurConnection = null; // If they didn't return something, don't allow this client. if (root == null) { @@ -265,16 +262,18 @@ public abstract class MediaBrowserService extends Service { ConnectionRecord connection = new ConnectionRecord( service, pkg, pid, uid, rootHints, callbacks, root); - service.mConnections.put(b, connection); + service.mServiceState.mConnections.put(b, connection); b.linkToDeath(connection, 0); - if (service.mSession != null) { - callbacks.onConnect(connection.root.getRootId(), - service.mSession, connection.root.getExtras()); + 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.mConnections.remove(b); + service.mServiceState.mConnections.remove(b); } } } @@ -294,7 +293,7 @@ public abstract class MediaBrowserService extends Service { final IBinder b = callbacks.asBinder(); // Clear out the old subscriptions. We are getting new ones. - final ConnectionRecord old = service.mConnections.remove(b); + final ConnectionRecord old = service.mServiceState.mConnections.remove(b); if (old != null) { // TODO old.callbacks.asBinder().unlinkToDeath(old, 0); @@ -322,7 +321,7 @@ public abstract class MediaBrowserService extends Service { final IBinder b = callbacks.asBinder(); // Get the record for the connection - final ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "addSubscription for callback that isn't registered id=" + id); @@ -353,7 +352,7 @@ public abstract class MediaBrowserService extends Service { public void run() { final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id); @@ -379,7 +378,7 @@ public abstract class MediaBrowserService extends Service { @Override public void run() { final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); return; @@ -393,13 +392,13 @@ public abstract class MediaBrowserService extends Service { @Override public void onCreate() { super.onCreate(); - mBinder = new ServiceBinder(this); + mServiceState.mBinder = new ServiceBinder(this); } @Override public IBinder onBind(Intent intent) { if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mBinder; + return mServiceState.mBinder; } return null; } @@ -524,14 +523,14 @@ public abstract class MediaBrowserService extends Service { if (token == null) { throw new IllegalArgumentException("Session token may not be null."); } - if (mSession != null) { + if (mServiceState.mSession != null) { throw new IllegalStateException("The session token has already been set."); } - mSession = token; + mServiceState.mSession = token; mHandler.post(new Runnable() { @Override public void run() { - Iterator<ConnectionRecord> iter = mConnections.values().iterator(); + Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator(); while (iter.hasNext()) { ConnectionRecord connection = iter.next(); try { @@ -551,7 +550,7 @@ public abstract class MediaBrowserService extends Service { * or if it has been destroyed. */ public @Nullable MediaSession.Token getSessionToken() { - return mSession; + return mServiceState.mSession; } /** @@ -567,11 +566,12 @@ public abstract class MediaBrowserService extends Service { * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED */ public final Bundle getBrowserRootHints() { - if (mCurConnection == null) { + ConnectionRecord curConnection = mServiceState.mCurConnection; + if (curConnection == null) { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); + return curConnection.rootHints == null ? null : new Bundle(curConnection.rootHints); } /** @@ -582,11 +582,12 @@ public abstract class MediaBrowserService extends Service { * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) */ public final RemoteUserInfo getCurrentBrowserInfo() { - if (mCurConnection == null) { + ConnectionRecord curConnection = mServiceState.mCurConnection; + if (curConnection == null) { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); + return new RemoteUserInfo(curConnection.pkg, curConnection.pid, curConnection.uid); } /** @@ -626,8 +627,8 @@ public abstract class MediaBrowserService extends Service { mHandler.post(new Runnable() { @Override public void run() { - for (IBinder binder : mConnections.keySet()) { - ConnectionRecord connection = mConnections.get(binder); + for (IBinder binder : mServiceState.mConnections.keySet()) { + ConnectionRecord connection = mServiceState.mConnections.get(binder); List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId); if (callbackList != null) { @@ -717,7 +718,7 @@ public abstract class MediaBrowserService extends Service { new Result<List<MediaBrowser.MediaItem>>(parentId) { @Override void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { + 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); @@ -747,13 +748,13 @@ public abstract class MediaBrowserService extends Service { } }; - mCurConnection = connection; + mServiceState.mCurConnection = connection; if (options == null) { onLoadChildren(parentId, result); } else { onLoadChildren(parentId, result, options); } - mCurConnection = null; + mServiceState.mCurConnection = null; if (!result.isDone()) { throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" @@ -767,7 +768,7 @@ public abstract class MediaBrowserService extends Service { new Result<MediaBrowser.MediaItem>(itemId) { @Override void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { + 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); @@ -784,9 +785,9 @@ public abstract class MediaBrowserService extends Service { } }; - mCurConnection = connection; + mServiceState.mCurConnection = connection; onLoadItem(itemId, result); - mCurConnection = null; + mServiceState.mCurConnection = null; if (!result.isDone()) { throw new IllegalStateException("onLoadItem must call detach() or sendResult()" @@ -881,4 +882,22 @@ public abstract class MediaBrowserService extends Service { return mExtras; } } + + /** + * Holds all state associated with {@link #mSession}. + * + * <p>This class decouples the state associated with the session from the lifecycle of the + * service. This allows us to put the service in a valid state once the session is released + * (which is an irrecoverable invalid state). More details about this in b/185136506. + */ + private static class ServiceState { + + // Fields accessed from any caller thread. + @Nullable private MediaSession.Token mSession; + @Nullable private ServiceBinder mBinder; + + // Fields accessed from mHandler only. + @NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); + @Nullable private ConnectionRecord mCurConnection; + } } diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java index f105ae9cc33e..236b1fdb1989 100644 --- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java @@ -45,8 +45,10 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @RequiresFlagsEnabled(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION) public final class FadeManagerConfigurationUnitTest { - private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000; - private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000; + private static final long DEFAULT_FADE_OUT_DURATION_MS = + FadeManagerConfiguration.getDefaultFadeOutDurationMillis(); + private static final long DEFAULT_FADE_IN_DURATION_MS = + FadeManagerConfiguration.getDefaultFadeInDurationMillis(); private static final long TEST_FADE_OUT_DURATION_MS = 1_500; private static final long TEST_FADE_IN_DURATION_MS = 750; private static final int TEST_INVALID_USAGE = -10; @@ -259,16 +261,6 @@ public final class FadeManagerConfigurationUnitTest { } @Test - public void testSetFadeState_toEnableAuto() { - final int fadeStateAuto = FadeManagerConfiguration.FADE_STATE_ENABLED_AUTO; - FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder() - .setFadeState(fadeStateAuto).build(); - - expect.withMessage("Fade state when enabled for audio").that(fmc.getFadeState()) - .isEqualTo(fadeStateAuto); - } - - @Test public void testSetFadeState_toInvalid_fails() { IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> new FadeManagerConfiguration.Builder() @@ -310,13 +302,13 @@ public final class FadeManagerConfigurationUnitTest { } @Test - public void testSetFadeVolShaperConfig_withNullVolumeShaper_getsNull() { + public void testSetFadeOutVolShaperConfig_withNullVolumeShaper_getsNull() { FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder(mFmc) .setFadeOutVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE, /* VolumeShaper.Configuration= */ null) .setFadeInVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE, /* VolumeShaper.Configuration= */ null) - .clearFadeableUsage(AudioAttributes.USAGE_MEDIA).build(); + .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_MEDIA).build(); expect.withMessage("Fade out volume shaper config set with null value") .that(fmc.getFadeOutVolumeShaperConfigForAudioAttributes( @@ -547,31 +539,13 @@ public final class FadeManagerConfigurationUnitTest { } @Test - public void testClearFadeableUsage() { - final int usageToClear = AudioAttributes.USAGE_MEDIA; - List<Integer> updatedUsages = new ArrayList<>(mFmc.getFadeableUsages()); - updatedUsages.remove((Integer) usageToClear); - - FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration - .Builder(mFmc).clearFadeableUsage(usageToClear).build(); - - expect.withMessage("Clear fadeable usage").that(updatedFmc.getFadeableUsages()) - .containsExactlyElementsIn(updatedUsages); - } - - @Test - public void testClearFadeableUsage_withInvalidUsage_fails() { - FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc); - - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> - fmcBuilder.clearFadeableUsage(TEST_INVALID_USAGE) - ); + public void testClearFadeableUsages() { + FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(mFmc) + .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .build(); - FadeManagerConfiguration fmc = fmcBuilder.build(); - expect.withMessage("Clear invalid usage").that(thrown).hasMessageThat() - .contains("Invalid usage"); - expect.withMessage("Fadeable usages").that(fmc.getFadeableUsages()) - .containsExactlyElementsIn(mFmc.getFadeableUsages()); + expect.withMessage("Clear fadeable usages").that(updatedFmc.getFadeableUsages()) + .containsExactlyElementsIn(List.of(AudioAttributes.USAGE_VOICE_COMMUNICATION)); } @Test @@ -673,7 +647,7 @@ public final class FadeManagerConfigurationUnitTest { } @Test - public void testClearUnfadeableContentType() { + public void testClearUnfadeableContentTypes() { List<Integer> unfadeableContentTypes = new ArrayList<>(Arrays.asList( AudioAttributes.CONTENT_TYPE_MOVIE, AudioAttributes.CONTENT_TYPE_SONIFICATION @@ -682,23 +656,10 @@ public final class FadeManagerConfigurationUnitTest { FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder() .setUnfadeableContentTypes(unfadeableContentTypes) - .clearUnfadeableContentType(contentTypeToClear).build(); + .clearUnfadeableContentTypes().build(); - unfadeableContentTypes.remove((Integer) contentTypeToClear); expect.withMessage("Unfadeable content types").that(updatedFmc.getUnfadeableContentTypes()) - .containsExactlyElementsIn(unfadeableContentTypes); - } - - @Test - public void testClearUnfadeableContentType_withInvalidContentType_fails() { - FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc); - - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> - fmcBuilder.clearUnfadeableContentType(TEST_INVALID_CONTENT_TYPE).build() - ); - - expect.withMessage("Invalid content type exception").that(thrown).hasMessageThat() - .contains("Invalid content type"); + .isEmpty(); } @Test @@ -735,7 +696,7 @@ public final class FadeManagerConfigurationUnitTest { } @Test - public void testClearUnfadebaleUid() { + public void testClearUnfadebaleUids() { final List<Integer> unfadeableUids = List.of( TEST_UID_1, TEST_UID_2 @@ -744,10 +705,9 @@ public final class FadeManagerConfigurationUnitTest { .setUnfadeableUids(unfadeableUids).build(); FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(fmc) - .clearUnfadeableUid(TEST_UID_1).build(); + .clearUnfadeableUids().build(); - expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids()) - .isEqualTo(List.of(TEST_UID_2)); + expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids()).isEmpty(); } @Test diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java index d256aead97e8..b5cf011c32a6 100644 --- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java @@ -33,12 +33,12 @@ import android.os.Looper; import android.os.Process; import android.os.SystemProperties; import android.provider.DeviceConfig; +import android.sysprop.CrashRecoveryProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.LongArrayQueue; -import android.util.MathUtils; import android.util.Slog; import android.util.Xml; @@ -128,18 +128,9 @@ public class PackageWatchdog { @VisibleForTesting static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5; + @VisibleForTesting static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); - // These properties track individual system server boot events, and are reset once the boot - // threshold is met, or the boot loop trigger window is exceeded between boot events. - private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; - private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start"; - - // These properties track multiple calls made to observers tracking boot loops. They are reset - // when the de-escalation window is exceeded between boot events. - private static final String PROP_BOOT_MITIGATION_WINDOW_START = "sys.boot_mitigation_start"; - private static final String PROP_BOOT_MITIGATION_COUNT = "sys.boot_mitigation_count"; - private long mNumberOfNativeCrashPollsRemaining; private static final int DB_VERSION = 1; @@ -1702,42 +1693,45 @@ public class PackageWatchdog { setCount(0); } - private int getCount() { - return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0); + protected int getCount() { + return CrashRecoveryProperties.rescueBootCount().orElse(0); } - private void setCount(int count) { - SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count)); + protected void setCount(int count) { + CrashRecoveryProperties.rescueBootCount(count); } public long getStart() { - return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0); + return CrashRecoveryProperties.rescueBootStart().orElse(0L); } public int getMitigationCount() { - return SystemProperties.getInt(PROP_BOOT_MITIGATION_COUNT, 0); + return CrashRecoveryProperties.bootMitigationCount().orElse(0); } public void setStart(long start) { - setPropertyStart(PROP_RESCUE_BOOT_START, start); + CrashRecoveryProperties.rescueBootStart(getStartTime(start)); } public void setMitigationStart(long start) { - setPropertyStart(PROP_BOOT_MITIGATION_WINDOW_START, start); + CrashRecoveryProperties.bootMitigationStart(getStartTime(start)); } public long getMitigationStart() { - return SystemProperties.getLong(PROP_BOOT_MITIGATION_WINDOW_START, 0); + return CrashRecoveryProperties.bootMitigationStart().orElse(0L); } public void setMitigationCount(int count) { - SystemProperties.set(PROP_BOOT_MITIGATION_COUNT, Integer.toString(count)); + CrashRecoveryProperties.bootMitigationCount(count); + } + + private static long constrain(long amount, long low, long high) { + return amount < low ? low : (amount > high ? high : amount); } - public void setPropertyStart(String property, long start) { + public long getStartTime(long start) { final long now = mSystemClock.uptimeMillis(); - final long newStart = MathUtils.constrain(start, 0, now); - SystemProperties.set(property, Long.toString(newStart)); + return constrain(start, 0, now); } public void saveMitigationCountToMetadata() { diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java index 5d03ef578995..6766afc5e45a 100644 --- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java +++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java @@ -37,6 +37,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; +import android.sysprop.CrashRecoveryProperties; import android.text.TextUtils; import android.util.ArraySet; import android.util.ExceptionUtils; @@ -75,12 +76,6 @@ import java.util.concurrent.TimeUnit; */ public class RescueParty { @VisibleForTesting - static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; - static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset"; - static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot"; - static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted"; - static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset"; - @VisibleForTesting static final int LEVEL_NONE = 0; @VisibleForTesting static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1; @@ -93,8 +88,6 @@ public class RescueParty { @VisibleForTesting static final int LEVEL_FACTORY_RESET = 5; @VisibleForTesting - static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; - @VisibleForTesting static final String TAG = "RescueParty"; @VisibleForTesting static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); @@ -131,7 +124,7 @@ public class RescueParty { private static boolean isDisabled() { // Check if we're explicitly enabled for testing - if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { + if (CrashRecoveryProperties.enableRescueParty().orElse(false)) { return false; } @@ -177,11 +170,11 @@ public class RescueParty { } static boolean isFactoryResetPropertySet() { - return SystemProperties.getBoolean(PROP_ATTEMPTING_FACTORY_RESET, false); + return CrashRecoveryProperties.attemptingFactoryReset().orElse(false); } static boolean isRebootPropertySet() { - return SystemProperties.getBoolean(PROP_ATTEMPTING_REBOOT, false); + return CrashRecoveryProperties.attemptingReboot().orElse(false); } /** @@ -440,7 +433,7 @@ public class RescueParty { case LEVEL_WARM_REBOOT: // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. - SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true"); + CrashRecoveryProperties.attemptingReboot(true); runnable = () -> { try { PowerManager pm = context.getSystemService(PowerManager.class); @@ -462,9 +455,9 @@ public class RescueParty { if (isRebootPropertySet()) { break; } - SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true"); + CrashRecoveryProperties.attemptingFactoryReset(true); long now = System.currentTimeMillis(); - SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(now)); + CrashRecoveryProperties.lastFactoryResetTimeMs(now); runnable = new Runnable() { @Override public void run() { @@ -514,10 +507,10 @@ public class RescueParty { private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { // No need to reset Settings again if they are already reset in the current level once. - if (SystemProperties.getInt(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, LEVEL_NONE) >= level) { + if (CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE) >= level) { return; } - SystemProperties.set(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, Integer.toString(level)); + CrashRecoveryProperties.maxRescueLevelAttempted(level); // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; @@ -732,7 +725,7 @@ public class RescueParty { * Will return {@code false} if a factory reset was already offered recently. */ private boolean shouldThrottleReboot() { - Long lastResetTime = SystemProperties.getLong(PROP_LAST_FACTORY_RESET_TIME_MS, 0); + Long lastResetTime = CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); long now = System.currentTimeMillis(); long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 50322f09640f..dd74a2a978b2 100644 --- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -34,6 +34,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.os.SystemProperties; +import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -70,7 +71,6 @@ import java.util.function.Consumer; final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final String TAG = "RollbackPackageHealthObserver"; private static final String NAME = "rollback-observer"; - private static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot"; private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; @@ -450,7 +450,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { markStagedSessionHandled(rollback.getRollbackId()); // Wait for all pending staged sessions to get handled before rebooting. if (isPendingStagedSessionsEmpty()) { - SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true"); + CrashRecoveryProperties.attemptingReboot(true); mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); } } diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt index 9a2cf61d2b86..e7d1072c352d 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt @@ -40,7 +40,7 @@ fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? = Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this") } if (showCancel) { - val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName) + val appLabel = packageManager.appLabel(cancelUiRequest.packageName) if (appLabel == null) { Log.d(TAG, "Received UI cancel request with an invalid package name.") null diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index b13df61005e2..30973879de10 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -119,7 +119,7 @@ class CredentialManagerRepo( val cancellationRequest = getCancelUiRequest(intent) val cancelUiRequestState = cancellationRequest?.let { - CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName)) + CancelUiRequestState(getAppLabel(context.getPackageManager(), it.packageName)) } initialUiState = when (requestInfo?.type) { diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 26a97cd30c77..4771237d449f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -135,7 +135,7 @@ class CredentialSelectorActivity : ComponentActivity() { Log.d( Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" + " ui = $shouldShowCancellationUi") - val appDisplayName = getAppLabel(packageManager, cancelUiRequest.appPackageName) + val appDisplayName = getAppLabel(packageManager, cancelUiRequest.packageName) if (!shouldShowCancellationUi) { this.finish() } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 6c5a984f20f7..f4da1e6c4770 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -72,7 +72,7 @@ class CredentialSelectorViewModel( init { uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INIT, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.packageName) } /**************************************************************************/ @@ -107,7 +107,7 @@ class CredentialSelectorViewModel( if (this.credManRepo.requestInfo?.token != credManRepo.requestInfo?.token) { this.uiMetrics.resetInstanceId() this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_NEW_REQUEST, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.packageName) } } @@ -189,7 +189,7 @@ class CredentialSelectorViewModel( private fun onInternalError() { Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state") this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INTERNAL_ERROR, - credManRepo.requestInfo?.appPackageName) + credManRepo.requestInfo?.packageName) credManRepo.onParsingFailureCancel() uiState = uiState.copy(dialogState = DialogState.COMPLETE) } @@ -399,6 +399,6 @@ class CredentialSelectorViewModel( @Composable fun logUiEvent(uiEventEnum: UiEventEnum) { - this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.appPackageName) + this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName) } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 64595e2642f5..997c45e84180 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -195,7 +195,7 @@ class GetFlowUtils { } return com.android.credentialmanager.getflow.RequestDisplayInfo( appName = originName?.ifEmpty { null } - ?: getAppLabel(context.packageManager, requestInfo.appPackageName) + ?: getAppLabel(context.packageManager, requestInfo.packageName) ?: return null, preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials, preferIdentityDocUi = getCredentialRequest.data.getBoolean( @@ -269,7 +269,7 @@ class CreateFlowUtils { return null } val appLabel = originName?.ifEmpty { null } - ?: getAppLabel(context.packageManager, requestInfo.appPackageName) + ?: getAppLabel(context.packageManager, requestInfo.packageName) ?: return null val createCredentialRequest = requestInfo.createCredentialRequest ?: return null val createCredentialRequestJetpack = CreateCredentialRequest.createFrom( diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index f44b16104f99..aed985ec1218 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <style name="TextAppearance.PreferenceTitle.SettingsLib" parent="@android:style/TextAppearance.Material.Subhead"> - <item name="android:textColor">@color/settingslib_text_color_primary</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20sp</item> </style> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index fd2c076136c0..ca93fa54faa5 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vra elke keer"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Hierdie foon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Hierdie tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Hierdie tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokluidspreker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kon nie \'n nuwe gebruiker skep nie"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kon nie ’n nuwe gas skep nie"</string> <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Voeg gebruiker by"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index dbd1af532b4d..ccaea8f933e2 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ሁልጊዜ ጠይቅ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ይህ ስልክ"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ይህ ጡባዊ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ይህ ጡባዊ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"የመትከያ ድምፅ ማውጫ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"አዲስ ተጠቃሚን መፍጠር አልተሳካም"</string> <string name="add_guest_failed" msgid="8074548434469843443">"አዲስ እንግዳ መፍጠር አልተሳካም"</string> <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ተጠቃሚን አክል"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 5afd9dce0079..00bc6137cd44 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"السؤال في كل مرة"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"هذا الهاتف"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"هذا الجهاز اللوحي"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"هذا الجهاز اللوحي"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"مكبّر صوت بقاعدة إرساء"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"تعذّر إنشاء مستخدم جديد."</string> <string name="add_guest_failed" msgid="8074548434469843443">"تعذّر إنشاء جلسة ضيف جديدة."</string> <string name="user_nickname" msgid="262624187455825083">"اللقب"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"إضافة مستخدم"</string> <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 47dad1f81455..59c87d2ff062 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্ৰতিবাৰতে সোধক"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফ’নটো"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই টেবলেটটো"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই টেবলেটটো"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ড’ক স্পীকাৰ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰিব পৰা নগ’ল"</string> <string name="add_guest_failed" msgid="8074548434469843443">"নতুন অতিথি সৃষ্টি কৰিব পৰা নগ’ল"</string> <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ব্যৱহাৰকাৰী যোগ দিয়ক"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ দিয়ক"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 0ee8b897d28e..7dfe3bef233c 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Həmişə soruşulsun"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Deaktiv edilənə qədər"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu planşet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu planşet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok dinamiki"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yeni istifadəçi yaratmaq alınmadı"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yeni qonaq yaratmaq alınmadı"</string> <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"İstifadəçi əlavə edin"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 0cf688703376..8786acb36be1 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik bazne stanice"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Pravljenje novog korisnika nije uspelo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Pravljenje novog gosta nije uspelo"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Dodaj korisnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index fe0e60c29301..4a1f939617be 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Заўсёды пытацца"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Гэты тэлефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Гэты планшэт"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Гэты планшэт"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Дынамік док-станцыі"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Знешняя прылада"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не ўдалося стварыць новага карыстальніка"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не ўдалося стварыць новага госця"</string> <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Дадаць карыстальніка"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index b7e90bccf3fb..3bc531ff94a1 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Да се пита винаги"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"До изключване"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Този телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Този таблет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Този таблет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Високоговорител докинг станция"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Неуспешно създаване на нов потребител"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Създаването на нов гост не бе успешно"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Добавяне на потребител"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 4e57e5f8f68c..1342aa3e43ed 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্রতিবার জিজ্ঞেস করা হবে"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফোন"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই ট্যাবলেট"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই ট্যাবলেট"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ডক স্পিকার"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"নতুন ব্যবহারকারী যোগ করা যায়নি"</string> <string name="add_guest_failed" msgid="8074548434469843443">"নতুন অতিথি তৈরি করা যায়নি"</string> <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ব্যবহারকারীর অ্যাকাউন্ট যোগ করুন"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index d2bcfb6d9ed9..996856257213 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kreiranje novog korisnika nije uspjelo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kreiranje novog gosta nije uspjelo"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Dodavanje korisnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index a556cf155dce..98226f62ddd3 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pregunta sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Aquest telèfon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Aquesta tauleta"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Aquesta tauleta"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base d\'altaveu"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"No s\'ha pogut crear l\'usuari"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No s\'ha pogut crear un convidat"</string> <string name="user_nickname" msgid="262624187455825083">"Àlies"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Afegeix un usuari"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 45e089ee0f54..96272a8bd91f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pokaždé se zeptat"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokud funkci nevypnete"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok s reproduktorem"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nového uživatele se nepodařilo vytvořit"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Vytvoření nového hosta se nezdařilo"</string> <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Přidat uživatele"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 89d34a6cf201..c1e200622426 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spørg hver gang"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Denne tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Denne tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockhøjttaler"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string> @@ -598,7 +598,7 @@ <string name="user_add_user_title" msgid="5457079143694924885">"Vil du tilføje en ny bruger?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enhed med andre ved at oprette ekstra brugere. Hver bruger har sit personlige område, som kan tilpasses med apps, baggrund osv. Brugerne kan også justere enhedsindstillinger, som for eksempel Wi-Fi, som påvirker alle.\n\nNår du tilføjer en ny bruger, skal vedkommende konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle andre brugere. Indstillinger og tjenester for hjælpefunktioner overføres muligvis ikke til den nye bruger."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle de andre brugere."</string> - <string name="user_grant_admin_title" msgid="5157031020083343984">"Vil du tildele denne bruger administratorrettigheder?"</string> + <string name="user_grant_admin_title" msgid="5157031020083343984">"Vil du tildele denne bruger administratorrettigheder?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administratorer har særlige rettigheder, som andre brugere ikke har. En administrator kan administrere alle brugere, opdatere eller gendanne denne enhed, skifte indstillinger, se alle installerede apps og tildele eller tilbagekalde andres administratorrettigheder."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Tildel administratorrettigheder"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Vil du konfigurere brugeren nu?"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Der kunne ikke oprettes en ny bruger"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Der kunne ikke oprettes en ny gæst"</string> <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Tilføj bruger"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 58713d651452..f6be521667d6 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Jedes Mal fragen"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Dieses Smartphone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Dieses Tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dieses Tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock-Lautsprecher"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externes Gerät"</string> @@ -596,10 +596,10 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Nutzer"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Eingeschränkte Profile"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Neuen Nutzer hinzufügen?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kannst dieses Gerät zusammen mit anderen nutzen, indem du weitere Nutzer erstellst. Jeder erhält einen eigenen Bereich, in dem er Apps, den Hintergrund usw. personalisieren kann. Außerdem lassen sich Geräteeinstellungen wie WLAN ändern, die sich auf alle Nutzer auswirken.\n\nWenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren. Bedienungshilfen-Einstellungen und -Dienste werden möglicherweise nicht auf den neuen Nutzer übertragen."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kannst dieses Gerät zusammen mit anderen nutzen, indem du weitere Nutzer erstellst. Jeder erhält einen eigenen Bereich, in dem er Apps, den Hintergrund usw. personalisieren kann. Außerdem lassen sich Geräteeinstellungen wie WLAN ändern, die sich auf alle Nutzer auswirken.\n\nWenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren. Einstellungen und Dienste für die Bedienungshilfen werden möglicherweise nicht auf den neuen Nutzer übertragen."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Diesen Nutzer als Administrator festlegen?"</string> - <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und für andere Administratorberechtigungen gewähren oder aufheben."</string> + <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und anderen Administratorberechtigungen gewähren oder diese widerrufen."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Als Administrator festlegen"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Nutzer jetzt einrichten?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"Die Person muss Zugang zum Gerät haben und bereit sein, ihren Bereich einzurichten."</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nutzer konnte nicht erstellt werden"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Fehler beim Erstellen eines neuen Gasts"</string> <string name="user_nickname" msgid="262624187455825083">"Alias"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Nutzer hinzufügen"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string> @@ -633,7 +635,7 @@ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gastmodus beenden?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hierdurch werden Apps und Daten der aktuellen Gastsitzung gelöscht"</string> <string name="grant_admin" msgid="4323199171790522574">"Ja, als Administrator festlegen"</string> - <string name="not_grant_admin" msgid="3557849576157702485">"Nein, nicht als Administrator festlegen"</string> + <string name="not_grant_admin" msgid="3557849576157702485">"Nein, nicht als Administrator festlegen"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Beenden"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gastaktivität speichern?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Speichere Aktivitäten der aktuellen Sitzung oder lösche alle Apps und Daten"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index b7df3688e24f..61e8ab43c850 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Να ερωτώμαι κάθε φορά"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Μέχρι την απενεργοποίηση"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Αυτό το tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Αυτό το tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Ηχείο βάσης σύνδεσης"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Η δημιουργία νέου χρήστη απέτυχε"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Αποτυχία δημιουργίας νέου επισκέπτη"</string> <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Προσθήκη χρήστη"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 66aaaf5ab9bc..b140131d5c94 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Add user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index ad683172e507..6a118876e75e 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string> @@ -619,6 +619,7 @@ <string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <string name="edit_user_info_message" msgid="5199468585059260053">"Your name and picture will be visible to anyone that uses this device."</string> <string name="user_add_user" msgid="7876449291500212468">"Add user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 66aaaf5ab9bc..b140131d5c94 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Add user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 66aaaf5ab9bc..b140131d5c94 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Add user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 98106bfe6ca6..f4bdc120bc49 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string> @@ -619,6 +619,7 @@ <string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <string name="edit_user_info_message" msgid="5199468585059260053">"Your name and picture will be visible to anyone that uses this device."</string> <string name="user_add_user" msgid="7876449291500212468">"Add user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 5e496cd75c3d..fa83538a144d 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Conector de la bocina"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"No se pudo crear el usuario nuevo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No se pudo crear un nuevo invitado"</string> <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Agregar usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 3ce371f9056c..2248d6509b8b 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altavoz base"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"No se ha podido crear el usuario"</string> <string name="add_guest_failed" msgid="8074548434469843443">"No se ha podido crear un nuevo invitado"</string> <string name="user_nickname" msgid="262624187455825083">"Apodo"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Añadir usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 115a64a05749..d7f6c5ac5bbc 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Küsi iga kord"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"See telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"See tahvelarvuti"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"See tahvelarvuti"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doki kõlar"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Uue kasutaja loomine ebaõnnestus"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Uue külalise loomine ei õnnestunud"</string> <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Lisa kasutaja"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index f1e507e5c2f8..12803c5f8db1 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Galdetu beti"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefono hau"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tableta hau"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tableta hau"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Oinarri bozgorailuduna"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string> @@ -582,7 +582,7 @@ <string name="shared_data_summary" msgid="5516326713822885652">"Ikusi eta aldatu datu partekatuak"</string> <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Ez dago erabiltzaile honen datu partekaturik."</string> <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Errore bat gertatu da datu partekatuak eskuratzean. Saiatu berriro."</string> - <string name="blob_id_text" msgid="8680078988996308061">"Partekatutako datuen IDa: <xliff:g id="BLOB_ID">%d</xliff:g>"</string> + <string name="blob_id_text" msgid="8680078988996308061">"Partekatutako datuenidentifikatzailea: <xliff:g id="BLOB_ID">%d</xliff:g>"</string> <string name="blob_expires_text" msgid="7882727111491739331">"Iraungitze-data: <xliff:g id="DATE">%s</xliff:g>"</string> <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Errore bat gertatu da datu partekatuak ezabatzean."</string> <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Ez da eskuratu alokairu-hitzarmenik datu partekatu hauetarako. Ezabatu egin nahi dituzu?"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ezin izan da sortu erabiltzailea"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ezin izan da sortu beste gonbidatu bat"</string> <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Gehitu erabiltzaile bat"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatu bat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 68d9ed156353..86193bdb7e55 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"هربار پرسیده شود"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانیکه آن را خاموش کنید"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"این تلفن"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"این رایانه لوحی"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"این رایانه لوحی"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"بلندگوی پایه اتصال"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"کاربر جدید ایجاد نشد"</string> <string name="add_guest_failed" msgid="8074548434469843443">"مهمان جدید ایجاد نشد"</string> <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"افزودن کاربر"</string> <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 13cface55fc7..320357c2781c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Kysy aina"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tämä puhelin"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tämä tabletti"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tämä tabletti"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Telinekaiutin"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Uuden käyttäjän luominen epäonnistui"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Uutta vierasta ei voitu luoda"</string> <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Lisää käyttäjä"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 5d23510c7635..e0285b54fe09 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur du socle"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Impossible de créer un utilisateur"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un nouvel invité"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index ae6d48e90bef..63b47472dd42 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à ce que vous le désactiviez"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur station d\'accueil"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Échec de la création d\'un utilisateur"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un profil invité"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 75eb21fbc86d..7088dc6bc6e4 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tableta"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tableta"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altofalante da base"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Non se puido crear un novo usuario"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Produciuse un erro ao crear o convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Alcume"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Engadir usuario"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index e4770634aa6e..77fc573fc164 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"દર વખતે પૂછો"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"આ ફોન"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"આ ટૅબ્લેટ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"આ ટૅબ્લેટ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ડૉક સ્પીકર"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"નવો વપરાશકર્તા બનાવવામાં નિષ્ફળ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"નવી અતિથિ બનાવવામાં નિષ્ફળ રહ્યાં"</string> <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"વપરાશકર્તા ઉમેરો"</string> <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 0e91a37554bb..4b1da1d14ada 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"हर बार पूछें"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"जब तक इसे बंद नहीं किया जाता"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यह फ़ोन"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यह टैबलेट"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यह टैबलेट"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"नया उपयोगकर्ता जोड़ा नहीं जा सका"</string> <string name="add_guest_failed" msgid="8074548434469843443">"नया मेहमान खाता नहीं बनाया जा सका"</string> <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"उपयोगकर्ता जोड़ें"</string> <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान को हटाएं"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 259779ab9c9f..6c7e4144bf4b 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Do isključivanja"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Izrada novog korisnika nije uspjela"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Izrada novog gosta nije uspjela"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Dodajte korisnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodajte gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 2ffb08caecca..cf5bed2a6104 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Mindig kérdezzen rá"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ez a telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ez a táblagép"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ez a táblagép"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhangszóró"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Az új felhasználó létrehozása sikertelen"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Az új vendég létrehozása nem sikerült"</string> <string name="user_nickname" msgid="262624187455825083">"Becenév"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Felhasználó hozzáadása"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 1b28a0d36934..ad2ccaee3add 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ամեն անգամ հարցնել"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև անջատեք"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Այս հեռախոսը"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Այս պլանշետը"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Այս պլանշետը"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Դոկ-կայանով բարձրախոս"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Չհաջողվեց ստեղծել նոր օգտատեր"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Չհաջողվեց նոր հյուր ստեղծել"</string> <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Ավելացնել օգտատեր"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 9e9c79cea726..ce85a0d28df1 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -181,7 +181,7 @@ <string name="launch_defaults_none" msgid="8049374306261262709">"Tidak ada setelan default"</string> <string name="tts_settings" msgid="8130616705989351312">"Setelan text-to-speech"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Output text-to-speech"</string> - <string name="tts_default_rate_title" msgid="3964187817364304022">"Kecepatan ucapan"</string> + <string name="tts_default_rate_title" msgid="3964187817364304022">"Kecepatan bicara"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Kecepatan teks diucapkan"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"Tinggi nada"</string> <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Memengaruhi nada ucapan yang disintesis"</string> @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Selalu tanya"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ponsel ini"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker dok"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baru"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tamu baru"</string> <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Tambahkan pengguna"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 071597ac31b8..30dba227163f 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spyrja í hvert skipti"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Þessi sími"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Þessi spjaldtölva"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Þessi spjaldtölva"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Hátalaradokka"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ekki tókst að stofna nýjan notanda"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ekki tókst að búa til nýjan gest"</string> <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Bæta notanda við"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 05404322be12..5fe9e4cda45a 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Chiedi ogni volta"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Questo smartphone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Questo tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo smartphone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Questo tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base con altoparlante"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Creazione nuovo utente non riuscita"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Impossibile creare un nuovo ospite"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Aggiungi utente"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 6dbf7abe9ebe..92e0516841ae 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"יש לשאול בכל פעם"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"הטלפון הזה"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"הטאבלט הזה"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"הטאבלט הזה"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"הרמקול של אביזר העגינה"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"לא ניתן היה ליצור משתמש חדש"</string> <string name="add_guest_failed" msgid="8074548434469843443">"יצירת אורח חדש נכשלה"</string> <string name="user_nickname" msgid="262624187455825083">"כינוי"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"הוספת משתמש"</string> <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index fbdca5407c1a..5c7a9dd48f4e 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"毎回確認"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"OFF にするまで"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"このデバイス"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"このタブレット"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"このデバイス"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"このタブレット"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ホルダー スピーカー"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部デバイス"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"新しいユーザーを作成できませんでした"</string> <string name="add_guest_failed" msgid="8074548434469843443">"新しいゲストを作成できませんでした"</string> <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ユーザーを追加"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 0f02b449b432..c70787f2d233 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ყოველთვის მკითხეთ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ეს ტელეფონი"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ეს ტაბლეტი"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ეს ტაბლეტი"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"დინამიკის სამაგრი"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"ახალი მომხმარებლის შექმნა ვერ მოხერხდა"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ახალი სტუმრის შექმნა ვერ მოხერხდა"</string> <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"მომხმარებლის დამატება"</string> <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index cc560b896af9..9eab6b0052ec 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -551,9 +551,10 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Әрдайым сұрау"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Осы телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Осы планшет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string> + <!-- no translation found for media_transfer_this_device_name_tablet (2975593806278422086) --> + <skip /> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Қондыру динамигі"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string> @@ -619,6 +620,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Жаңа пайдаланушы жасалмады."</string> <string name="add_guest_failed" msgid="8074548434469843443">"Жаңа қонақ профилі жасалмады."</string> <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Пайдаланушы қосу"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index bf8870763a6a..de00d948e01b 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"សួរគ្រប់ពេល"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"រហូតទាល់តែអ្នកបិទ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ថេប្លេតនេះ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ថេប្លេតនេះ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ឧបករណ៍បំពងសំឡេងដែលមានជើងភ្ជាប់"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"មិនអាចបង្កើតអ្នកប្រើប្រាស់ថ្មីបានទេ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"មិនអាចបង្កើតភ្ញៀវថ្មីបានទេ"</string> <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"បញ្ចូលអ្នកប្រើប្រាស់"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 0288e9207e0d..93432b1c2129 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ಡಾಕ್ ಸ್ಪೀಕರ್"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string> <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 432f4ed4d8fe..c30e11252d64 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"항상 확인"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"사용 중지할 때까지"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"이 휴대전화"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"태블릿"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"이 태블릿"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"도크 스피커"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"새 사용자를 만들지 못함"</string> <string name="add_guest_failed" msgid="8074548434469843443">"새 게스트 생성 실패"</string> <string name="user_nickname" msgid="262624187455825083">"닉네임"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"사용자 추가"</string> <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index aa685a20816e..45773b932c0d 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ар дайым суралсын"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ушул телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ушул планшет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ушул планшет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док бекети үчүн динамик"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Жаңы колдонуучу түзүлбөй калды"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Жаңы конок түзүлгөн жок"</string> <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Колдонуучу кошуу"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 4e47201d7dee..5dedbdec374d 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ຖາມທຸກເທື່ອ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ແທັບເລັດນີ້"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ແທັບເລັດນີ້"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ແທ່ນວາງລຳໂພງ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"ສ້າງຜູ້ໃຊ້ໃໝ່ບໍ່ສຳເລັດ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ສ້າງແຂກໃໝ່ບໍ່ສຳເລັດ"</string> <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index d0d95075d5a3..9cc69b0d4473 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Klausti kaskart"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis telefonas"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetinis kompiuteris"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetinis kompiuteris"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doko garsiakalbis"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nepavyko sukurti naujo naudotojo"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nepavyko sukurti naujo gesto"</string> <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Pridėti naudotoją"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 43a940b0fad9..2bf1bee19392 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vaicāt katru reizi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis tālrunis"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetdators"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetdators"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doka skaļrunis"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Neizdevās izveidot jaunu lietotāju"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Neizdevās izveidot jaunu viesa profilu"</string> <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Pievienot lietotāju"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index db5bdf4bd279..3f1fa533d648 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Прашувај секогаш"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овој телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овој таблет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овој таблет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док со звучник"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не успеа да создаде нов корисник"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не успеа создавањето нов гостин"</string> <string name="user_nickname" msgid="262624187455825083">"Прекар"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Додајте корисник"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 947ab917881e..396547b18a2e 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"എപ്പോഴും ചോദിക്കുക"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ഈ ഫോൺ"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ഈ ടാബ്ലെറ്റ്"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ഈ ടാബ്ലെറ്റ്"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ഡോക്ക് സ്പീക്കർ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"പുതിയ ഉപയോക്താവിനെ സൃഷ്ടിക്കാനായില്ല"</string> <string name="add_guest_failed" msgid="8074548434469843443">"പുതിയ അതിഥിയെ സൃഷ്ടിക്കാനായില്ല"</string> <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ഉപയോക്താവിനെ ചേർക്കുക"</string> <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 787ce976e236..f1f3a90666b2 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Тухай бүрд асуух"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Таныг унтраах хүртэл"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Энэ утас"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Энэ таблет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Энэ таблет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Суурилуулагчийн чанга яригч"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Шинэ хэрэглэгч үүсгэж чадсангүй"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Шинэ зочин үүсгэж чадсангүй"</string> <string name="user_nickname" msgid="262624187455825083">"Хоч"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Хэрэглэгч нэмэх"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 00482ea8a15f..562004fd396b 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक वेळी विचारा"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"हा फोन"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"हा टॅबलेट"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"हा टॅबलेट"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"नवीन वापरकर्ता तयार करता आला नाही"</string> <string name="add_guest_failed" msgid="8074548434469843443">"नवीन अतिथी तयार करता आला नाही"</string> <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"वापरकर्ता जोडा"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index bd3eb997183d..264ab3552b0e 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Tanya setiap kali"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Sehingga anda matikan"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefon ini"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Pembesar suara dok"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baharu"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tetamu baharu"</string> <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Tambah pengguna"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index c18b273bd9bf..d839a4544440 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"အမြဲမေးရန်"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ဤဖုန်း"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ဤတက်ဘလက်"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ဤတက်ဘလက်"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"အထိုင် စပီကာ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"အသုံးပြုသူအသစ် ပြုလုပ်၍မရပါ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ဧည့်သည်သစ် ပြုလုပ်၍မရပါ"</string> <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည် ထည့်ရန်"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ရှားရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index b3c429502e72..dc326f0e4414 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spør hver gang"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefonen"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Dette nettbrettet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dette nettbrettet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhøyttaler"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kunne ikke opprette noen ny bruker"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kunne ikke opprette en ny gjest"</string> <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Legg til bruker"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Legg til gjest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 377078deeb76..d962cece54ce 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक पटक सोधियोस्"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले अफ नगरेसम्म"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यो फोन"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यो ट्याब्लेट"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यो ट्याब्लेट"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डक स्पिकर"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"नयाँ प्रयोगकर्ता सिर्जना गर्न सकिएन"</string> <string name="add_guest_failed" msgid="8074548434469843443">"नयाँ अतिथि बनाउन सकिएन"</string> <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f5767229a332..40c343c8a21b 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Altijd vragen"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je uitzet"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Deze telefoon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Deze tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Deze tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockspeaker"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Kan geen nieuwe gebruiker maken"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Kan geen nieuwe gast maken"</string> <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Gebruiker toevoegen"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 30bb05012dd4..133e5c752657 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ଏହି ଟାବଲେଟ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ଏହି ଟାବଲେଟ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ଡକ ସ୍ପିକର"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବାକୁ ବିଫଳ ହେଲା"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ଜଣେ ନୂଆ ଅତିଥି ତିଆରି କରିବାରେ ବିଫଳ ହୋଇଛି"</string> <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ୟୁଜର ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index d25b9217184b..160a43e10c4d 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ਇਹ ਟੈਬਲੈੱਟ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ਇਹ ਟੈਬਲੈੱਟ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ਡੌਕ ਸਪੀਕਰ"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string> <string name="add_guest_failed" msgid="8074548434469843443">"ਨਵਾਂ ਮਹਿਮਾਨ ਪ੍ਰੋਫਾਈਲ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string> <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 72a1b2c6e46f..6669c22b6533 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Zawsze pytaj"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ten telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ten tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ten tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Głośnik ze stacją dokującą"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nie udało się utworzyć nowego użytkownika"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nie udało się utworzyć nowego gościa"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Dodaj użytkownika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 814215e90fdb..0b44fc9fabdc 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar visitante"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover visitante"</string> @@ -693,7 +695,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Padrão"</string> <string name="turn_screen_on_title" msgid="3266937298097573424">"Ligar tela"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir que a tela seja ligada"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ligue a tela. Se permitido, o app vai poder ligar a tela a qualquer momento sem uma intent explícita."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ative a tela. Com sua autorização, o app vai poder ligar a tela a qualquer momento, sem você pedir."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 1d7b6094938e..de0eb7d9f4e8 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telemóvel"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altifalante estação carregam."</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo utilizador"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Alcunha"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Adicionar utilizador"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 814215e90fdb..0b44fc9fabdc 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar visitante"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover visitante"</string> @@ -693,7 +695,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Padrão"</string> <string name="turn_screen_on_title" msgid="3266937298097573424">"Ligar tela"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permitir que a tela seja ligada"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ligue a tela. Se permitido, o app vai poder ligar a tela a qualquer momento sem uma intent explícita."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permitir que um app ative a tela. Com sua autorização, o app vai poder ligar a tela a qualquer momento, sem você pedir."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index def9d8953839..848a5f6760a8 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Întreabă de fiecare dată"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivezi"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Acest telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Această tabletă"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Această tabletă"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Difuzorul dispozitivului de andocare"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nu s-a creat noul utilizator"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nu s-a putut crea un invitat nou"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adaugă un invitat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Șterge invitatul"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 5cc1a5b3b863..166e7cbf3f74 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Всегда спрашивать"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Этот смартфон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Этот планшет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Этот планшет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Колонка с док-станцией"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не удалось создать пользователя"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не удалось создать гостя."</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Добавить пользователя"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 0d9ca372b64a..d87792db2fb3 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"සෑම විටම ඉල්ලන්න"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ඔබ ක්රියාවිරහිත කරන තුරු"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"මෙම දුරකථනය"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"මෙම ටැබ්ලටය"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"මෙම ටැබ්ලටය"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ඩොක් ස්පීකරය"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"නව පරිශීලකයෙකු තැනීමට අසමත් විය"</string> <string name="add_guest_failed" msgid="8074548434469843443">"නව අමුත්තකු තැනීම අසාර්ථක විය"</string> <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"පරිශීලකයා එක් කරන්න"</string> <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index a9b518d5018e..33293f83cbf4 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vždy sa opýtať"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefón"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Nového použív. sa nepodarilo vytvoriť"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Nového hosťa sa nepodarilo vytvoriť"</string> <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Pridať používateľa"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index f739fbe0a7f8..1ec258c59aaf 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vedno vprašaj"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ta telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ta tablični računalnik"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ta tablični računalnik"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvočnik nosilca"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Ustvarjanje novega uporabnika ni uspelo."</string> <string name="add_guest_failed" msgid="8074548434469843443">"Ustvarjanje novega gosta ni uspelo."</string> <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Dodaj uporabnika"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstrani gosta"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index a722ad15aec3..9ebfd6079ce1 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pyet çdo herë"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Derisa ta çaktivizosh"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ky telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ky tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Krijimi i një përdoruesi të ri dështoi"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Profili i vizitorit të ri nuk u krijua"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Shto përdorues"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto vizitor"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq vizitorin"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 66e448bc0aaa..50e534132e51 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Питај сваки пут"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Док не искључите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овај телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овај таблет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овај таблет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Звучник базне станице"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Прављење новог корисника није успело"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Прављење новог госта није успело"</string> <string name="user_nickname" msgid="262624187455825083">"Надимак"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Додај корисника"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 82567bfe6c45..2d71c29922f6 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Fråga varje gång"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Den här telefonen"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Den här surfplattan"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Den här surfplattan"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockningsstationens högtalare"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Det gick inte att skapa en ny användare"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Det gick inte att skapa en ny gäst"</string> <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Lägg till användare"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 0d1ef461e57b..8c064950dbf9 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Uliza kila wakati"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hadi utakapoizima"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Simu hii"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Kompyuta kibao hii"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Kishikwambi hiki"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Spika ya kituo"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Imeshindwa kuweka mtumiaji mpya"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Imeshindwa kuunda wasifu mpya wa mgeni"</string> <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Ongeza mtumiaji"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index a5ce169a08a3..ceeb6035f2df 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ஒவ்வொரு முறையும் கேள்"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"இந்த மொபைல்"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"இந்த டேப்லெட்"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"இந்த டேப்லெட்"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"டாக் ஸ்பீக்கர்"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"புதிய பயனரை உருவாக்க முடியவில்லை"</string> <string name="add_guest_failed" msgid="8074548434469843443">"புதிய விருந்தினரை உருவாக்க முடியவில்லை"</string> <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"பயனரைச் சேர்"</string> <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index ce432b6e72c2..cb3f8d30bd3d 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ప్రతిసారి అడగాలి"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్ చేసే వరకు"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ఈ ఫోన్"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ఈ టాబ్లెట్"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ఈ టాబ్లెట్"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"డాక్ స్పీకర్"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్టర్నల్ పరికరం"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"కొత్త యూజర్ను క్రియేట్ చేయడం విఫలమైంది"</string> <string name="add_guest_failed" msgid="8074548434469843443">"కొత్త అతిథిని క్రియేట్ చేయడం విఫలమైంది"</string> <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"యూజర్ను జోడించండి"</string> <string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్ను జోడించండి"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"గెస్ట్ను తీసివేయండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 183130b53d9e..9617c2a9bca8 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ถามทุกครั้ง"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"จนกว่าคุณจะปิด"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"แท็บเล็ตเครื่องนี้"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"แท็บเล็ตเครื่องนี้"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"แท่นวางลำโพง"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"สร้างผู้ใช้ใหม่ไม่ได้"</string> <string name="add_guest_failed" msgid="8074548434469843443">"สร้างผู้เข้าร่วมใหม่ไม่สำเร็จ"</string> <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"เพิ่มผู้ใช้"</string> <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 53f75cbb5198..a551e2a4289c 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Magtanong palagi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hanggang sa i-off mo"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ang teleponong ito"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ang tablet na ito"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ang tablet na ito"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker ng dock"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Hindi nakagawa ng bagong user"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Hindi nakagawa ng bagong guest"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Magdagdag ng user"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 8e491a16bc07..54675f14a88e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Her zaman sor"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu tablet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu tablet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Yuva hoparlörü"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yeni kullanıcı oluşturulamadı"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yeni misafir oluşturulamadı"</string> <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Kullanıcı ekle"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 45ed50878cf5..355f566d0c9d 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Запитувати щоразу"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Цей телефон"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Цей планшет"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Цей планшет"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамік док-станції"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Не вдалося створити користувача"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Не вдалося створити гостя"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Додати користувача"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 020680e69e21..5c3a0676ac1b 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ہر بار پوچھیں"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"یہ فون"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"یہ ٹیبلیٹ"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"یہ ٹیبلیٹ"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ڈاک اسپیکر"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"نیا صارف بنانے میں ناکام"</string> <string name="add_guest_failed" msgid="8074548434469843443">"نیا مہمان بنانے میں ناکام"</string> <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"صارف کو شامل کریں"</string> <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index b46fca57d150..950a24e0c43c 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Har safar so‘ralsin"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Rejimdan chiqilgunicha"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Shu telefon"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Shu planshet"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Shu planshet"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok-stansiyali karnay"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yangi foydalanuvchi yaratilmadi"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yangi mehmon yaratilmadi"</string> <string name="user_nickname" msgid="262624187455825083">"Nik"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Foydalanuvchi kiritish"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index c471546eabc2..0328e27a7832 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Luôn hỏi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Cho đến khi bạn tắt"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Điện thoại này"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Máy tính bảng này"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Máy tính bảng này"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Loa có gắn đế"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string> @@ -594,7 +594,7 @@ <string name="user_add_user_item_summary" msgid="5748424612724703400">"Người dùng có ứng dụng và nội dung riêng của mình"</string> <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Bạn có thể hạn chế quyền truy cập vào ứng dụng và nội dung từ tài khoản của bạn"</string> <string name="user_add_user_item_title" msgid="2394272381086965029">"Người dùng"</string> - <string name="user_add_profile_item_title" msgid="3111051717414643029">"Tiểu sử bị hạn chế"</string> + <string name="user_add_profile_item_title" msgid="3111051717414643029">"Hồ sơ bị hạn chế"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Thêm người dùng mới?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Bạn có thể chia sẻ thiết bị này với người khác bằng cách tạo thêm người dùng. Mỗi người dùng sẽ có không gian riêng của mình. Họ có thể tùy chỉnh không gian riêng đó bằng các ứng dụng, hình nền, v.v. Người dùng cũng có thể điều chỉnh các tùy chọn cài đặt thiết bị có ảnh hưởng đến tất cả mọi người, chẳng hạn như Wi‑Fi.\n\nKhi bạn thêm người dùng mới, họ cần thiết lập không gian của mình.\n\nMọi người dùng đều có thể cập nhật ứng dụng cho tất cả người dùng khác. Các dịch vụ và các tùy chọn cài đặt hỗ trợ tiếp cận có thể không chuyển sang người dùng mới."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Khi bạn thêm người dùng mới, người đó cần thiết lập không gian của mình.\n\nMọi người dùng đều có thể cập nhật ứng dụng cho tất cả người dùng khác."</string> @@ -603,15 +603,15 @@ <string name="user_grant_admin_button" msgid="5441486731331725756">"Đặt làm quản trị viên"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Thiết lập người dùng ngay bây giờ?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"Đảm bảo người dùng có mặt để tự thiết lập không gian của mình trên thiết bị"</string> - <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Thiết lập tiểu sử ngay bây giờ?"</string> + <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Thiết lập hồ sơ ngay bây giờ?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Thiết lập ngay"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"Để sau"</string> <string name="user_add_user_type_title" msgid="551279664052914497">"Thêm"</string> <string name="user_new_user_name" msgid="60979820612818840">"Người dùng mới"</string> - <string name="user_new_profile_name" msgid="2405500423304678841">"Tiểu sử mới"</string> + <string name="user_new_profile_name" msgid="2405500423304678841">"Hồ sơ mới"</string> <string name="user_info_settings_title" msgid="6351390762733279907">"Thông tin người dùng"</string> <string name="profile_info_settings_title" msgid="105699672534365099">"Thông tin hồ sơ"</string> - <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string> + <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo hồ sơ bị hạn chế, bạn sẽ cần thiết lập một màn hình khoá để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Không tạo được người dùng mới"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Không tạo được khách mới"</string> <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Thêm người dùng"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa khách"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index e9774e11617f..6a35264e42b9 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都询问"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"这部手机"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"这台平板电脑"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"这部平板电脑"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"无法创建新用户"</string> <string name="add_guest_failed" msgid="8074548434469843443">"未能创建新的访客"</string> <string name="user_nickname" msgid="262624187455825083">"昵称"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"添加用户"</string> <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 1e3394d82261..6f49688f2b63 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直至你關閉為止"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"此手機"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"此平板電腦"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"無法建立新使用者"</string> <string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 8643fde22819..0985041701cd 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直到你關閉為止"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"這支手機"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"這台平板電腦"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"座架喇叭"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"無法建立新的使用者"</string> <string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index af859526f94e..cce45ea35073 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -551,9 +551,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Buza njalo"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Le foni"</string> - <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Le thebulethi"</string> - <!-- no translation found for media_transfer_this_device_name (8899776297775466649) --> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string> + <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Le thebhulethi"</string> + <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) --> <skip /> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Isipikha sentuba"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string> @@ -619,6 +619,8 @@ <string name="add_user_failed" msgid="4809887794313944872">"Yehlulekile ukudala umsebenzisi omusha"</string> <string name="add_guest_failed" msgid="8074548434469843443">"Yehlulekile ukusungula isimenywa esisha"</string> <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string> + <!-- no translation found for edit_user_info_message (5199468585059260053) --> + <skip /> <string name="user_add_user" msgid="7876449291500212468">"Engeza umsebenzisi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt new file mode 100644 index 000000000000..2a4658bc69a1 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt @@ -0,0 +1,69 @@ +/* + * 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.data.repository + +import android.media.AudioDeviceAttributes +import android.media.Spatializer +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext + +interface SpatializerRepository { + + /** + * Returns true when Spatial audio feature is supported for the [audioDeviceAttributes] and + * false the otherwise. + */ + suspend fun isAvailableForDevice(audioDeviceAttributes: AudioDeviceAttributes): Boolean + + /** Returns a list [AudioDeviceAttributes] that are compatible with spatial audio. */ + suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> + + /** Adds a [audioDeviceAttributes] to [getCompatibleDevices] list. */ + suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) + + /** Removes a [audioDeviceAttributes] to [getCompatibleDevices] list. */ + suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) +} + +class SpatializerRepositoryImpl( + private val spatializer: Spatializer, + private val backgroundContext: CoroutineContext, +) : SpatializerRepository { + + override suspend fun isAvailableForDevice( + audioDeviceAttributes: AudioDeviceAttributes + ): Boolean { + return withContext(backgroundContext) { + spatializer.isAvailableForDevice(audioDeviceAttributes) + } + } + + override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> = + withContext(backgroundContext) { spatializer.compatibleAudioDevices } + + override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) { + withContext(backgroundContext) { + spatializer.addCompatibleAudioDevice(audioDeviceAttributes) + } + } + + override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) { + withContext(backgroundContext) { + spatializer.removeCompatibleAudioDevice(audioDeviceAttributes) + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt new file mode 100644 index 000000000000..c3cc340d9cd8 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.media.domain.interactor + +import android.media.AudioDeviceAttributes +import com.android.settingslib.media.data.repository.SpatializerRepository + +class SpatializerInteractor(private val repository: SpatializerRepository) { + + suspend fun isAvailable(audioDeviceAttributes: AudioDeviceAttributes): Boolean = + repository.isAvailableForDevice(audioDeviceAttributes) + + /** Checks if spatial audio is enabled for the [audioDeviceAttributes]. */ + suspend fun isEnabled(audioDeviceAttributes: AudioDeviceAttributes): Boolean = + repository.getCompatibleDevices().contains(audioDeviceAttributes) + + /** Enblaes or disables spatial audio for [audioDeviceAttributes]. */ + suspend fun setEnabled(audioDeviceAttributes: AudioDeviceAttributes, isEnabled: Boolean) { + if (isEnabled) { + repository.addCompatibleDevice(audioDeviceAttributes) + } else { + repository.removeCompatibleDevice(audioDeviceAttributes) + } + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt new file mode 100644 index 000000000000..3f52f2494dfc --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt @@ -0,0 +1,45 @@ +/* + * 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.domain.interactor + +import android.media.AudioDeviceAttributes +import com.android.settingslib.media.data.repository.SpatializerRepository + +class FakeSpatializerRepository : SpatializerRepository { + + private val availabilityByDevice: MutableMap<AudioDeviceAttributes, Boolean> = mutableMapOf() + private val compatibleDevices: MutableList<AudioDeviceAttributes> = mutableListOf() + + override suspend fun isAvailableForDevice( + audioDeviceAttributes: AudioDeviceAttributes + ): Boolean = availabilityByDevice.getOrDefault(audioDeviceAttributes, false) + + override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> = + compatibleDevices + + override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) { + compatibleDevices.add(audioDeviceAttributes) + } + + override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) { + compatibleDevices.remove(audioDeviceAttributes) + } + + fun setIsAvailable(audioDeviceAttributes: AudioDeviceAttributes, isAvailable: Boolean) { + availabilityByDevice[audioDeviceAttributes] = isAvailable + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt new file mode 100644 index 000000000000..a44baeb174bf --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.media.domain.interactor + +import android.media.AudioDeviceAttributes +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SpatializerInteractorTest { + + private val testScope = TestScope() + private val underTest = SpatializerInteractor(FakeSpatializerRepository()) + + @Test + fun setEnabledFalse_isEnabled_false() { + testScope.runTest { + underTest.setEnabled(deviceAttributes, false) + + assertThat(underTest.isEnabled(deviceAttributes)).isFalse() + } + } + + @Test + fun setEnabledTrue_isEnabled_true() { + testScope.runTest { + underTest.setEnabled(deviceAttributes, true) + + assertThat(underTest.isEnabled(deviceAttributes)).isTrue() + } + } + + private companion object { + val deviceAttributes = AudioDeviceAttributes(0, 0, "test_device") + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index e99fcc92dea7..84ef6e51a00b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -914,6 +914,9 @@ <!-- Permission required for Cts test ScreenRecordingCallbackTests --> <uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" /> + <!-- Permissions required for CTS test - GrammaticalInflectionManagerTest --> + <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index f877d7a89b92..12e8f574e906 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -262,6 +262,9 @@ <uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" /> + <!-- Activity Manager --> + <uses-permission android:name="android.permission.SET_THEME_OVERLAY_CONTROLLER_READY" /> + <!-- accessibility --> <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" /> <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" /> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 2ad7192a3248..6a4f92c92d6f 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -227,6 +227,17 @@ flag { } flag { + name: "truncated_status_bar_icons_fix" + namespace: "systemui" + description: "Fixes the status bar icons being trunacted due to the status bar window height " + "not being updated after certain rotations" + bug: "323299264" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "compose_bouncer" namespace: "systemui" description: "Use the new compose bouncer in SystemUI" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index 4973cafbb397..1b99e19eeb95 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -45,13 +45,13 @@ import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils import kotlin.math.roundToInt -private const val TAG = "ActivityLaunchAnimator" +private const val TAG = "ActivityTransitionAnimator" /** * A class that allows activities to be started in a seamless way from a view that is transforming * nicely into the starting window. */ -class ActivityLaunchAnimator( +class ActivityTransitionAnimator( /** The animator used when animating a View into an app. */ private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR, @@ -97,7 +97,7 @@ class ActivityLaunchAnimator( ) // TODO(b/288507023): Remove this flag. - @JvmField val DEBUG_LAUNCH_ANIMATION = Build.IS_DEBUGGABLE + @JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE private val DEFAULT_TRANSITION_ANIMATOR = TransitionAnimator(TIMINGS, INTERPOLATORS) private val DEFAULT_DIALOG_TO_APP_ANIMATOR = @@ -113,13 +113,13 @@ class ActivityLaunchAnimator( private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f) /** The time we wait before timing out the remote animation after starting the intent. */ - private const val LAUNCH_TIMEOUT = 1_000L + private const val TRANSITION_TIMEOUT = 1_000L /** * The time we wait before we Log.wtf because the remote animation was neither started or * cancelled by WM. */ - private const val LONG_LAUNCH_TIMEOUT = 5_000L + private const val LONG_TRANSITION_TIMEOUT = 5_000L } /** @@ -134,20 +134,20 @@ class ActivityLaunchAnimator( /** Top-level listener that can be used to notify all registered [listeners]. */ private val lifecycleListener = object : Listener { - override fun onLaunchAnimationStart() { - listeners.forEach { it.onLaunchAnimationStart() } + override fun onTransitionAnimationStart() { + listeners.forEach { it.onTransitionAnimationStart() } } - override fun onLaunchAnimationEnd() { - listeners.forEach { it.onLaunchAnimationEnd() } + override fun onTransitionAnimationEnd() { + listeners.forEach { it.onTransitionAnimationEnd() } } - override fun onLaunchAnimationProgress(linearProgress: Float) { - listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } + override fun onTransitionAnimationProgress(linearProgress: Float) { + listeners.forEach { it.onTransitionAnimationProgress(linearProgress) } } - override fun onLaunchAnimationCancelled() { - listeners.forEach { it.onLaunchAnimationCancelled() } + override fun onTransitionAnimationCancelled() { + listeners.forEach { it.onTransitionAnimationCancelled() } } } @@ -188,7 +188,7 @@ class ActivityLaunchAnimator( val callback = this.callback ?: throw IllegalStateException( - "ActivityLaunchAnimator.callback must be set before using this animator" + "ActivityTransitionAnimator.callback must be set before using this animator" ) val runner = createRunner(controller) val runnerDelegate = runner.delegate!! @@ -260,7 +260,7 @@ class ActivityLaunchAnimator( callOnIntentStartedOnMainThread(willAnimate) } } else { - if (DEBUG_LAUNCH_ANIMATION) { + if (DEBUG_TRANSITION_ANIMATION) { Log.d( TAG, "Calling controller.onIntentStarted(willAnimate=$willAnimate) " + @@ -293,7 +293,7 @@ class ActivityLaunchAnimator( } } - /** Add a [Listener] that can listen to launch animations. */ + /** Add a [Listener] that can listen to transition animations. */ fun addListener(listener: Listener) { listeners.add(listener) } @@ -340,24 +340,24 @@ class ActivityLaunchAnimator( } interface Listener { - /** Called when an activity launch animation started. */ - fun onLaunchAnimationStart() {} + /** Called when an activity transition animation started. */ + fun onTransitionAnimationStart() {} /** - * Called when an activity launch animation is finished. This will be called if and only if - * [onLaunchAnimationStart] was called earlier. + * Called when an activity transition animation is finished. This will be called if and only + * if [onTransitionAnimationStart] was called earlier. */ - fun onLaunchAnimationEnd() {} + fun onTransitionAnimationEnd() {} /** - * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after - * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called - * before the cancellation. + * The animation was cancelled. Note that [onTransitionAnimationEnd] will still be called + * after this if the animation was already started, i.e. if [onTransitionAnimationStart] was + * called before the cancellation. */ - fun onLaunchAnimationCancelled() {} + fun onTransitionAnimationCancelled() {} - /** Called when an activity launch animation made progress. */ - fun onLaunchAnimationProgress(linearProgress: Float) {} + /** Called when an activity transition animation made progress. */ + fun onTransitionAnimationProgress(linearProgress: Float) {} } /** @@ -383,9 +383,10 @@ class ActivityLaunchAnimator( // issues. if (view !is LaunchableView) { throw IllegalArgumentException( - "An ActivityLaunchAnimator.Controller was created from a View that does " + - "not implement LaunchableView. This can lead to subtle bugs where the" + - " visibility of the View we are launching from is not what we expected." + "An ActivityTransitionAnimator.Controller was created from a View that " + + "does not implement LaunchableView. This can lead to subtle bugs " + + "where the visibility of the View we are launching from is not what " + + "we expected." ) } @@ -398,7 +399,7 @@ class ActivityLaunchAnimator( return null } - return GhostedViewLaunchAnimatorController(view, cujType) + return GhostedViewTransitionAnimatorController(view, cujType) } } @@ -411,11 +412,11 @@ class ActivityLaunchAnimator( get() = false /** - * Whether the expandable controller by this [Controller] is below the launching window that - * is going to be animated. + * Whether the expandable controller by this [Controller] is below the window that is going + * to be animated. * - * This should be `false` when launching an app from the shade or status bar, given that - * they are drawn above all apps. This is usually `true` when using this launcher in a + * This should be `false` when animating an app from or to the shade or status bar, given + * that they are drawn above all apps. This is usually `true` when using this animator in a * normal app or a launcher, that are drawn below the animating activity/window. */ val isBelowAnimatingWindow: Boolean @@ -432,10 +433,11 @@ class ActivityLaunchAnimator( * after this if the animation was already started, i.e. if [onTransitionAnimationStart] was * called before the cancellation. * - * If this launch animation affected the occlusion state of the keyguard, WM will provide us - * with [newKeyguardOccludedState] so that we can set the occluded state appropriately. + * If this transition animation affected the occlusion state of the keyguard, WM will + * provide us with [newKeyguardOccludedState] so that we can set the occluded state + * appropriately. */ - fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {} + fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {} } /** @@ -449,24 +451,24 @@ class ActivityLaunchAnimator( ) : Listener { var cancelled = false - override fun onLaunchAnimationStart() { - delegate?.onLaunchAnimationStart() + override fun onTransitionAnimationStart() { + delegate?.onTransitionAnimationStart() } - override fun onLaunchAnimationProgress(linearProgress: Float) { - delegate?.onLaunchAnimationProgress(linearProgress) + override fun onTransitionAnimationProgress(linearProgress: Float) { + delegate?.onTransitionAnimationProgress(linearProgress) } - override fun onLaunchAnimationEnd() { - delegate?.onLaunchAnimationEnd() + override fun onTransitionAnimationEnd() { + delegate?.onTransitionAnimationEnd() if (!cancelled) { onAnimationComplete.invoke() } } - override fun onLaunchAnimationCancelled() { + override fun onTransitionAnimationCancelled() { cancelled = true - delegate?.onLaunchAnimationCancelled() + delegate?.onTransitionAnimationCancelled() onAnimationComplete.invoke() } } @@ -475,7 +477,7 @@ class ActivityLaunchAnimator( inner class Runner( controller: Controller, callback: Callback, - /** The animator to use to animate the window launch. */ + /** The animator to use to animate the window transition. */ transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR, /** Listener for animation lifecycle events. */ listener: Listener? = null @@ -543,7 +545,7 @@ class ActivityLaunchAnimator( private val callback: Callback, /** Listener for animation lifecycle events. */ private val listener: Listener? = null, - /** The animator to use to animate the window launch. */ + /** The animator to use to animate the window transition. */ private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR, /** @@ -574,8 +576,8 @@ class ActivityLaunchAnimator( private var animation: TransitionAnimator.Animation? = null /** - * A timeout to cancel the launch animation if the remote animation is not started or - * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started. + * A timeout to cancel the transition animation if the remote animation is not started or + * cancelled within [TRANSITION_TIMEOUT] milliseconds after the intent was started. * * Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise * it will be automatically converted when posted and we wouldn't be able to remove it after @@ -585,21 +587,22 @@ class ActivityLaunchAnimator( /** * A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't - * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was + * started or cancelled within [LONG_TRANSITION_TIMEOUT] milliseconds after the intent was * started. */ private var onLongTimeout = Runnable { Log.wtf( TAG, - "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT" + "The remote animation was neither cancelled or started within " + + "$LONG_TRANSITION_TIMEOUT" ) } @UiThread internal fun postTimeouts() { if (timeoutHandler != null) { - timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT) - timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT) + timeoutHandler.postDelayed(onTimeout, TRANSITION_TIMEOUT) + timeoutHandler.postDelayed(onLongTimeout, LONG_TRANSITION_TIMEOUT) } } @@ -670,14 +673,14 @@ class ActivityLaunchAnimator( Log.i(TAG, "Aborting the animation as no window is opening") iCallback?.invoke() - if (DEBUG_LAUNCH_ANIMATION) { + if (DEBUG_TRANSITION_ANIMATION) { Log.d( TAG, - "Calling controller.onLaunchAnimationCancelled() [no window opening]" + "Calling controller.onTransitionAnimationCancelled() [no window opening]" ) } - controller.onLaunchAnimationCancelled() - listener?.onLaunchAnimationCancelled() + controller.onTransitionAnimationCancelled() + listener?.onTransitionAnimationCancelled() return } @@ -720,27 +723,29 @@ class ActivityLaunchAnimator( val controller = object : Controller by delegate { override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { - listener?.onLaunchAnimationStart() + listener?.onTransitionAnimationStart() - if (DEBUG_LAUNCH_ANIMATION) { + if (DEBUG_TRANSITION_ANIMATION) { Log.d( TAG, - "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" + - "$isExpandingFullyAbove) [controller=$delegate]" + "Calling controller.onTransitionAnimationStart(" + + "isExpandingFullyAbove=$isExpandingFullyAbove) " + + "[controller=$delegate]" ) } delegate.onTransitionAnimationStart(isExpandingFullyAbove) } override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) { - listener?.onLaunchAnimationEnd() + listener?.onTransitionAnimationEnd() iCallback?.invoke() - if (DEBUG_LAUNCH_ANIMATION) { + if (DEBUG_TRANSITION_ANIMATION) { Log.d( TAG, - "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" + - "$isExpandingFullyAbove) [controller=$delegate]" + "Calling controller.onTransitionAnimationEnd(" + + "isExpandingFullyAbove=$isExpandingFullyAbove) " + + "[controller=$delegate]" ) } delegate.onTransitionAnimationEnd(isExpandingFullyAbove) @@ -758,7 +763,7 @@ class ActivityLaunchAnimator( } navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } - listener?.onLaunchAnimationProgress(linearProgress) + listener?.onTransitionAnimationProgress(linearProgress) delegate.onTransitionAnimationProgress(state, progress, linearProgress) } } @@ -904,7 +909,7 @@ class ActivityLaunchAnimator( } private fun onAnimationTimedOut() { - // The remote animation was cancelled by WM, so we already cancelled the launch + // The remote animation was cancelled by WM, so we already cancelled the transition // animation. if (cancelled) { return @@ -913,18 +918,21 @@ class ActivityLaunchAnimator( Log.w(TAG, "Remote animation timed out") timedOut = true - if (DEBUG_LAUNCH_ANIMATION) { - Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]") + if (DEBUG_TRANSITION_ANIMATION) { + Log.d( + TAG, + "Calling controller.onTransitionAnimationCancelled() [animation timed out]" + ) } - controller.onLaunchAnimationCancelled() - listener?.onLaunchAnimationCancelled() + controller.onTransitionAnimationCancelled() + listener?.onTransitionAnimationCancelled() } @UiThread override fun onAnimationCancelled() { removeTimeouts() - // The short timeout happened, so we already cancelled the launch animation. + // The short timeout happened, so we already cancelled the transition animation. if (timedOut) { return } @@ -934,14 +942,15 @@ class ActivityLaunchAnimator( animation?.cancel() - if (DEBUG_LAUNCH_ANIMATION) { + if (DEBUG_TRANSITION_ANIMATION) { Log.d( TAG, - "Calling controller.onLaunchAnimationCancelled() [remote animation cancelled]", + "Calling controller.onTransitionAnimationCancelled() [remote animation " + + "cancelled]", ) } - controller.onLaunchAnimationCancelled() - listener?.onLaunchAnimationCancelled() + controller.onTransitionAnimationCancelled() + listener?.onTransitionAnimationCancelled() } private fun IRemoteAnimationFinishedCallback.invoke() { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateTransitionAnimatorController.kt index b879ba0b1ece..e24656276111 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateTransitionAnimatorController.kt @@ -17,10 +17,10 @@ package com.android.systemui.animation /** - * A base class to easily create an implementation of [ActivityLaunchAnimator.Controller] which + * A base class to easily create an implementation of [ActivityTransitionAnimator.Controller] which * delegates most of its call to [delegate]. This is mostly useful for Java code which can't easily * create such a delegated class. */ -open class DelegateLaunchAnimatorController( - protected val delegate: ActivityLaunchAnimator.Controller -) : ActivityLaunchAnimator.Controller by delegate +open class DelegateTransitionAnimatorController( + protected val delegate: ActivityTransitionAnimator.Controller +) : ActivityTransitionAnimator.Controller by delegate diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 9a36960996b0..a3b3a0acd68d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -62,13 +62,14 @@ constructor( private val isForTesting: Boolean = false, ) { private companion object { - private val TIMINGS = ActivityLaunchAnimator.TIMINGS + private val TIMINGS = ActivityTransitionAnimator.TIMINGS // We use the same interpolator for X and Y axis to make sure the dialog does not move out // of the screen bounds during the animation. private val INTERPOLATORS = - ActivityLaunchAnimator.INTERPOLATORS.copy( - positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator + ActivityTransitionAnimator.INTERPOLATORS.copy( + positionXInterpolator = + ActivityTransitionAnimator.INTERPOLATORS.positionInterpolator ) } @@ -319,9 +320,9 @@ constructor( } /** - * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the - * dialog that contains [View]. Note that the dialog must have been shown using this animator, - * otherwise this method will return null. + * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from + * the dialog that contains [View]. Note that the dialog must have been shown using this + * animator, otherwise this method will return null. * * The returned controller will take care of dismissing the dialog at the right time after the * activity started, when the dialog to app animation is done (or when it is cancelled). If this @@ -333,7 +334,7 @@ constructor( fun createActivityLaunchController( view: View, cujType: Int? = null, - ): ActivityLaunchAnimator.Controller? { + ): ActivityTransitionAnimator.Controller? { val animatedDialog = openedDialogs.firstOrNull { it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl @@ -343,7 +344,7 @@ constructor( } /** - * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from + * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from * [dialog]. Note that the dialog must have been shown using this animator, otherwise this * method will return null. * @@ -357,7 +358,7 @@ constructor( fun createActivityLaunchController( dialog: Dialog, cujType: Int? = null, - ): ActivityLaunchAnimator.Controller? { + ): ActivityTransitionAnimator.Controller? { val animatedDialog = openedDialogs.firstOrNull { it.dialog == dialog } ?: return null return createActivityLaunchController(animatedDialog, cujType) } @@ -365,7 +366,7 @@ constructor( private fun createActivityLaunchController( animatedDialog: AnimatedDialog, cujType: Int? = null - ): ActivityLaunchAnimator.Controller? { + ): ActivityTransitionAnimator.Controller? { // At this point, we know that the intent of the caller is to dismiss the dialog to show // an app, so we disable the exit animation into the source because we will never want to // run it anyways. @@ -384,12 +385,12 @@ constructor( val dialogContentWithBackground = animatedDialog.dialogContentWithBackground ?: return null val controller = - ActivityLaunchAnimator.Controller.fromView(dialogContentWithBackground, cujType) + ActivityTransitionAnimator.Controller.fromView(dialogContentWithBackground, cujType) ?: return null // Wrap the controller into one that will instantly dismiss the dialog when the animation is // done or dismiss it normally (fading it out) if the animation is cancelled. - return object : ActivityLaunchAnimator.Controller by controller { + return object : ActivityTransitionAnimator.Controller by controller { override val isDialogLaunch = true override fun onIntentStarted(willAnimate: Boolean) { @@ -400,8 +401,8 @@ constructor( } } - override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { - controller.onLaunchAnimationCancelled() + override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) { + controller.onTransitionAnimationCancelled() enableDialogDismiss() dialog.dismiss() } @@ -632,7 +633,7 @@ private class AnimatedDialog( val background = dialogContentWithBackground.background originalDialogBackgroundColor = - GhostedViewLaunchAnimatorController.findGradientDrawable(background) + GhostedViewTransitionAnimatorController.findGradientDrawable(background) ?.color ?.defaultColor ?: Color.BLACK @@ -894,11 +895,11 @@ private class AnimatedDialog( if (isLaunching) { controller.createTransitionController() } else { - GhostedViewLaunchAnimatorController(dialogContentWithBackground!!) + GhostedViewTransitionAnimatorController(dialogContentWithBackground!!) } val endController = if (isLaunching) { - GhostedViewLaunchAnimatorController(dialogContentWithBackground!!) + GhostedViewTransitionAnimatorController(dialogContentWithBackground!!) } else { controller.createExitController() } @@ -967,7 +968,7 @@ private class AnimatedDialog( // Therefore we update the end state to the new position/size. Usually the // dialog dimension or position will change in the early frames, so changing the // end state shouldn't really be noticeable. - if (endController is GhostedViewLaunchAnimatorController) { + if (endController is GhostedViewTransitionAnimatorController) { endController.fillGhostedViewState(endState) } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt index c49a487c6766..2ba5948c50cc 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt @@ -21,14 +21,14 @@ import android.view.View /** A piece of UI that can be expanded into a Dialog or an Activity. */ interface Expandable { /** - * Create an [ActivityLaunchAnimator.Controller] that can be used to expand this [Expandable] - * into an Activity, or return `null` if this [Expandable] should not be animated (e.g. if it is - * currently not attached or visible). + * Create an [ActivityTransitionAnimator.Controller] that can be used to expand this + * [Expandable] into an Activity, or return `null` if this [Expandable] should not be animated + * (e.g. if it is currently not attached or visible). * * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor] * associated to the launch that will use this controller. */ - fun activityLaunchController(cujType: Int? = null): ActivityLaunchAnimator.Controller? + fun activityLaunchController(cujType: Int? = null): ActivityTransitionAnimator.Controller? /** * Create a [DialogLaunchAnimator.Controller] that can be used to expand this [Expandable] into @@ -49,8 +49,8 @@ interface Expandable { return object : Expandable { override fun activityLaunchController( cujType: Int?, - ): ActivityLaunchAnimator.Controller? { - return ActivityLaunchAnimator.Controller.fromView(view, cujType) + ): ActivityTransitionAnimator.Controller? { + return ActivityTransitionAnimator.Controller.fromView(view, cujType) } override fun dialogLaunchController( diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt index 03f10f9ac7e3..e4dc9beb51f8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt @@ -39,21 +39,21 @@ import java.util.LinkedList import kotlin.math.min import kotlin.math.roundToInt -private const val TAG = "GhostedViewLaunchAnimatorController" +private const val TAG = "GhostedViewTransitionAnimatorController" /** - * A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView] - * of [ghostedView] as well as an expandable background view, which are drawn and animated instead - * of the ghosted view. + * A base implementation of [ActivityTransitionAnimator.Controller] which creates a + * [ghost][GhostView] of [ghostedView] as well as an expandable background view, which are drawn and + * animated instead of the ghosted view. * * Important: [ghostedView] must be attached to a [ViewGroup] when calling this function and during * the animation. It must also implement [LaunchableView], otherwise an exception will be thrown * during this controller instantiation. * - * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView] + * Note: Avoid instantiating this directly and call [ActivityTransitionAnimator.Controller.fromView] * whenever possible instead. */ -open class GhostedViewLaunchAnimatorController +open class GhostedViewTransitionAnimatorController @JvmOverloads constructor( /** The view that will be ghosted and from which the background will be extracted. */ @@ -63,7 +63,7 @@ constructor( private val cujType: Int? = null, private var interactionJankMonitor: InteractionJankMonitor = InteractionJankMonitor.getInstance(), -) : ActivityLaunchAnimator.Controller { +) : ActivityTransitionAnimator.Controller { /** The container to which we will add the ghost view and expanding background. */ override var transitionContainer = ghostedView.rootView as ViewGroup diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt index e2a29ab41f61..e07f945e069b 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt @@ -69,7 +69,7 @@ internal constructor( } override fun createTransitionController(): TransitionAnimator.Controller { - val delegate = GhostedViewLaunchAnimatorController(source) + val delegate = GhostedViewTransitionAnimatorController(source) return object : TransitionAnimator.Controller by delegate { override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { // Remove the temporary ghost added by [startDrawingInOverlayOf]. Another @@ -95,7 +95,7 @@ internal constructor( } override fun createExitController(): TransitionAnimator.Controller { - return GhostedViewLaunchAnimatorController(source) + return GhostedViewTransitionAnimatorController(source) } override fun shouldAnimateExit(): Boolean { diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 2cd587ffbc45..59354c843447 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.surfaceeffects.turbulencenoise -import android.graphics.BlendMode import android.graphics.Color +import java.util.Random /** Turbulence noise animation configuration. */ data class TurbulenceNoiseAnimationConfig( @@ -26,6 +26,11 @@ data class TurbulenceNoiseAnimationConfig( /** Multiplier for the noise luma matte. Increase this for brighter effects. */ val luminosityMultiplier: Float = DEFAULT_LUMINOSITY_MULTIPLIER, + /** Initial noise offsets. */ + val noiseOffsetX: Float = random.nextFloat(), + val noiseOffsetY: Float = random.nextFloat(), + val noiseOffsetZ: Float = random.nextFloat(), + /** * Noise move speed variables. * @@ -45,18 +50,15 @@ data class TurbulenceNoiseAnimationConfig( val noiseMoveSpeedZ: Float = DEFAULT_NOISE_SPEED_Z, /** Color of the effect. */ - var color: Int = DEFAULT_COLOR, + val color: Int = DEFAULT_COLOR, /** Background color of the effect. */ val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR, - val opacity: Int = DEFAULT_OPACITY, val width: Float = 0f, val height: Float = 0f, val maxDuration: Float = DEFAULT_MAX_DURATION_IN_MILLIS, val easeInDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val easeOutDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val pixelDensity: Float = 1f, - val blendMode: BlendMode = DEFAULT_BLEND_MODE, - val onAnimationEnd: Runnable? = null, /** * Variants in noise. Higher number means more contrast; lower number means less contrast but * make the noise dimmed. You may want to increase the [lumaMatteBlendFactor] to compensate. @@ -68,7 +70,9 @@ data class TurbulenceNoiseAnimationConfig( * want to use this if you have made the noise softer using [lumaMatteBlendFactor]. Expected * range [0, 1]. */ - val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS + val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS, + /** Whether to flip the luma mask. */ + val shouldInverseNoiseLuminosity: Boolean = false ) { companion object { const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec @@ -76,11 +80,10 @@ data class TurbulenceNoiseAnimationConfig( const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f const val DEFAULT_NOISE_GRID_COUNT = 1.2f const val DEFAULT_NOISE_SPEED_Z = 0.3f - const val DEFAULT_OPACITY = 150 // full opacity is 255. const val DEFAULT_COLOR = Color.WHITE const val DEFAULT_LUMA_MATTE_BLEND_FACTOR = 1f const val DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS = 0f const val DEFAULT_BACKGROUND_COLOR = Color.BLACK - val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER + private val random = Random() } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt index b8f4b276dd6a..535c2d32ed09 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt @@ -69,12 +69,15 @@ class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoise * * <p>It plays ease-in, main, and ease-out animations in sequence. */ - fun play(config: TurbulenceNoiseAnimationConfig) { + fun play( + baseType: TurbulenceNoiseShader.Companion.Type, + config: TurbulenceNoiseAnimationConfig + ) { if (state != AnimationState.NOT_PLAYING) { return // Ignore if any of the animation is playing. } - turbulenceNoiseView.applyConfig(config) + turbulenceNoiseView.initShader(baseType, config) playEaseInAnimation() } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt index d3c57c91405a..30108ac779f2 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt @@ -22,10 +22,10 @@ import java.lang.Float.max /** * Shader that renders turbulence simplex noise, by default no octave. * - * @param useFractal whether to use fractal noise (4 octaves). + * @param baseType the base [Type] of the shader. */ -class TurbulenceNoiseShader(useFractal: Boolean = false) : - RuntimeShader(if (useFractal) FRACTAL_NOISE_SHADER else SIMPLEX_NOISE_SHADER) { +class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : + RuntimeShader(getShader(baseType)) { // language=AGSL companion object { private const val UNIFORMS = @@ -86,11 +86,34 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : return vec4(color * in_opacity, in_opacity); } """ - private const val SIMPLEX_NOISE_SHADER = ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SIMPLEX_SHADER private const val FRACTAL_NOISE_SHADER = ShaderUtilLibrary.SHADER_LIB + UNIFORMS + FRACTAL_SHADER + // TODO (b/282007590): Add NOISE_WITH_SPARKLE + + enum class Type { + SIMPLEX_NOISE, + SIMPLEX_NOISE_FRACTAL, + } + + fun getShader(type: Type): String { + return when (type) { + Type.SIMPLEX_NOISE -> SIMPLEX_NOISE_SHADER + Type.SIMPLEX_NOISE_FRACTAL -> FRACTAL_NOISE_SHADER + } + } + } + + /** Convenient way for updating multiple uniform values via config object. */ + fun applyConfig(config: TurbulenceNoiseAnimationConfig) { + setGridCount(config.gridCount) + setPixelDensity(config.pixelDensity) + setColor(config.color) + setBackgroundColor(config.backgroundColor) + setSize(config.width, config.height) + setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness) + setInverseNoiseLuminosity(config.shouldInverseNoiseLuminosity) } /** Sets the number of grid for generating noise. */ @@ -107,18 +130,19 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : setFloatUniform("in_pixelDensity", pixelDensity) } - /** Sets the noise color of the effect. */ + /** Sets the noise color of the effect. Alpha is ignored. */ fun setColor(color: Int) { setColorUniform("in_color", color) } - /** Sets the background color of the effect. */ + /** Sets the background color of the effect. Alpha is ignored. */ fun setBackgroundColor(color: Int) { setColorUniform("in_backgroundColor", color) } /** - * Sets the opacity to achieve fade in/ out of the animation. + * Sets the opacity of the effect. Not intended to set by the client as it is used for + * ease-in/out animations. * * Expected value range is [1, 0]. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt index 43d6504fce84..c59bc106ca91 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt @@ -19,12 +19,12 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.content.Context +import android.graphics.BlendMode import android.graphics.Canvas import android.graphics.Paint import android.util.AttributeSet import android.view.View import androidx.annotation.VisibleForTesting -import androidx.core.graphics.ColorUtils /** * View that renders turbulence noise effect. @@ -44,8 +44,8 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex private const val MS_TO_SEC = 0.001f } - private val turbulenceNoiseShader = TurbulenceNoiseShader() - private val paint = Paint().apply { this.shader = turbulenceNoiseShader } + private val paint = Paint() + @VisibleForTesting var turbulenceNoiseShader: TurbulenceNoiseShader? = null @VisibleForTesting var noiseConfig: TurbulenceNoiseAnimationConfig? = null @VisibleForTesting var currentAnimator: ValueAnimator? = null @@ -61,9 +61,7 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex /** Updates the color during the animation. No-op if there's no animation playing. */ internal fun updateColor(color: Int) { - noiseConfig?.let { - turbulenceNoiseShader.setColor(ColorUtils.setAlphaComponent(color, it.opacity)) - } + turbulenceNoiseShader?.setColor(color) } /** Plays the turbulence noise with no easing. */ @@ -73,24 +71,25 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex return } val config = noiseConfig!! + val shader = turbulenceNoiseShader!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.maxDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. - val initialX = turbulenceNoiseShader.noiseOffsetX - val initialY = turbulenceNoiseShader.noiseOffsetY - val initialZ = turbulenceNoiseShader.noiseOffsetZ + val initialX = shader.noiseOffsetX + val initialY = shader.noiseOffsetY + val initialZ = shader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC - turbulenceNoiseShader.setNoiseMove( + shader.setNoiseMove( initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) - turbulenceNoiseShader.setOpacity(config.luminosityMultiplier) + shader.setOpacity(config.luminosityMultiplier) invalidate() } @@ -115,27 +114,28 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex return } val config = noiseConfig!! + val shader = turbulenceNoiseShader!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeInDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. - val initialX = turbulenceNoiseShader.noiseOffsetX - val initialY = turbulenceNoiseShader.noiseOffsetY - val initialZ = turbulenceNoiseShader.noiseOffsetZ + val initialX = shader.noiseOffsetX + val initialY = shader.noiseOffsetY + val initialZ = shader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC val progress = updateListener.animatedValue as Float - turbulenceNoiseShader.setNoiseMove( + shader.setNoiseMove( offsetX + initialX + timeInSec * config.noiseMoveSpeedX, offsetY + initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) // TODO: Replace it with a better curve. - turbulenceNoiseShader.setOpacity(progress * config.luminosityMultiplier) + shader.setOpacity(progress * config.luminosityMultiplier) invalidate() } @@ -160,27 +160,28 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex return } val config = noiseConfig!! + val shader = turbulenceNoiseShader!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeOutDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. - val initialX = turbulenceNoiseShader.noiseOffsetX - val initialY = turbulenceNoiseShader.noiseOffsetY - val initialZ = turbulenceNoiseShader.noiseOffsetZ + val initialX = shader.noiseOffsetX + val initialY = shader.noiseOffsetY + val initialZ = shader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC val progress = updateListener.animatedValue as Float - turbulenceNoiseShader.setNoiseMove( + shader.setNoiseMove( initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) // TODO: Replace it with a better curve. - turbulenceNoiseShader.setOpacity((1f - progress) * config.luminosityMultiplier) + shader.setOpacity((1f - progress) * config.luminosityMultiplier) invalidate() } @@ -211,18 +212,22 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex /** Applies shader uniforms. Must be called before playing animation. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) - fun applyConfig(config: TurbulenceNoiseAnimationConfig) { + fun initShader( + baseType: TurbulenceNoiseShader.Companion.Type, + config: TurbulenceNoiseAnimationConfig + ) { noiseConfig = config - with(turbulenceNoiseShader) { - setGridCount(config.gridCount) - setColor(config.color) - setBackgroundColor(config.backgroundColor) - setSize(config.width, config.height) - setPixelDensity(config.pixelDensity) - setInverseNoiseLuminosity(inverse = false) - setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness) + if (turbulenceNoiseShader == null || turbulenceNoiseShader?.baseType != baseType) { + turbulenceNoiseShader = TurbulenceNoiseShader(baseType) + + paint.shader = turbulenceNoiseShader!! } - paint.blendMode = config.blendMode + turbulenceNoiseShader!!.applyConfig(config) + } + + /** Sets the blend mode of the View. */ + fun setBlendMode(blendMode: BlendMode) { + paint.blendMode = blendMode } internal fun clearConfig() { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt index 1020263ee7f6..84e5725b6ada 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import com.android.internal.jank.InteractionJankMonitor -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable @@ -135,7 +135,7 @@ internal class ExpandableControllerImpl( object : Expandable { override fun activityLaunchController( cujType: Int?, - ): ActivityLaunchAnimator.Controller? { + ): ActivityTransitionAnimator.Controller? { if (!isComposed.value) { return null } @@ -176,7 +176,7 @@ internal class ExpandableControllerImpl( linearProgress: Float ) { // We copy state given that it's always the same object that is mutated by - // ActivityLaunchAnimator. + // ActivityTransitionAnimator. animatorState.value = TransitionAnimator.State( state.top, @@ -256,11 +256,11 @@ internal class ExpandableControllerImpl( } } - /** Create an [ActivityLaunchAnimator.Controller] that can be used to animate activities. */ - private fun activityController(cujType: Int?): ActivityLaunchAnimator.Controller { + /** Create an [ActivityTransitionAnimator.Controller] that can be used to animate activities. */ + private fun activityController(cujType: Int?): ActivityTransitionAnimator.Controller { val delegate = transitionController() return object : - ActivityLaunchAnimator.Controller, TransitionAnimator.Controller by delegate { + ActivityTransitionAnimator.Controller, TransitionAnimator.Controller by delegate { override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { delegate.onTransitionAnimationStart(isExpandingFullyAbove) overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 4e72dfef82e1..090750e046a7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -312,12 +312,12 @@ private fun BoxScope.CommunalHubLazyGrid( contentType = { index -> list[index].key }, span = { index -> GridItemSpan(list[index].size.span) }, ) { index -> - val cardModifier = Modifier.width(Dimensions.CardWidth) val size = SizeF( Dimensions.CardWidth.value, list[index].size.dp().value, ) + val cardModifier = Modifier.size(width = size.width.dp, height = size.height.dp) if (viewModel.isEditMode && dragDropState != null) { val selected by remember(index) { derivedStateOf { list[index].key == selectedKey.value } } @@ -326,7 +326,6 @@ private fun BoxScope.CommunalHubLazyGrid( selected = selected, enabled = list[index] is CommunalContentModel.Widget, index = index, - size = size ) { isDragging -> CommunalContent( modifier = cardModifier, @@ -536,11 +535,10 @@ private fun CommunalContent( when (model) { is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, selected, widgetConfigurator, modifier) - is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(size) - is CommunalContentModel.CtaTileInViewMode -> - CtaTileInViewModeContent(viewModel, size, modifier) + is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(modifier) + is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier) is CommunalContentModel.CtaTileInEditMode -> - CtaTileInEditModeContent(size, modifier, onOpenWidgetPicker) + CtaTileInEditModeContent(modifier, onOpenWidgetPicker) is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, modifier) @@ -549,9 +547,9 @@ private fun CommunalContent( /** Creates an empty card used to highlight a particular spot on the grid. */ @Composable -fun HighlightedItem(size: SizeF, modifier: Modifier = Modifier) { +fun HighlightedItem(modifier: Modifier = Modifier) { Card( - modifier = modifier.size(Dp(size.width), Dp(size.height)), + modifier = modifier, colors = CardDefaults.cardColors(containerColor = Color.Transparent), border = BorderStroke(CardOutlineWidth, LocalAndroidColorScheme.current.tertiaryFixed), shape = RoundedCornerShape(16.dp) @@ -562,12 +560,11 @@ fun HighlightedItem(size: SizeF, modifier: Modifier = Modifier) { @Composable private fun CtaTileInViewModeContent( viewModel: BaseCommunalViewModel, - size: SizeF, modifier: Modifier = Modifier, ) { val colors = LocalAndroidColorScheme.current Card( - modifier = modifier.height(size.height.dp).padding(CardOutlineWidth), + modifier = modifier.padding(CardOutlineWidth), colors = CardDefaults.cardColors( containerColor = colors.primary, @@ -630,7 +627,6 @@ private fun CtaTileInViewModeContent( /** Presents a CTA tile at the end of the hub in edit mode, to add more widgets. */ @Composable private fun CtaTileInEditModeContent( - size: SizeF, modifier: Modifier = Modifier, onOpenWidgetPicker: (() -> Unit)? = null, ) { @@ -639,7 +635,7 @@ private fun CtaTileInEditModeContent( } val colors = LocalAndroidColorScheme.current Card( - modifier = modifier.height(size.height.dp).padding(CardOutlineWidth), + modifier = modifier.padding(CardOutlineWidth), colors = CardDefaults.cardColors(containerColor = Color.Transparent), border = BorderStroke(1.dp, colors.primary), shape = RoundedCornerShape(200.dp), @@ -677,12 +673,11 @@ private fun WidgetContent( modifier: Modifier = Modifier, ) { Box( - modifier = modifier.height(size.height.dp), + modifier = modifier, ) { val paddingInPx = with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() } AndroidView( - modifier = - modifier.align(Alignment.Center).allowGestures(allowed = !viewModel.isEditMode), + modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode), factory = { context -> val view = model.appWidgetHost diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index a1959532fbb9..beb8ddef949d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -16,7 +16,6 @@ package com.android.systemui.communal.ui.compose -import android.util.SizeF import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn @@ -36,7 +35,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput @@ -245,7 +243,6 @@ fun LazyGridItemScope.DraggableItem( index: Int, enabled: Boolean, selected: Boolean, - size: SizeF, modifier: Modifier = Modifier, content: @Composable (isDragging: Boolean) -> Unit ) { @@ -276,7 +273,7 @@ fun LazyGridItemScope.DraggableItem( enter = fadeIn(), exit = fadeOut() ) { - HighlightedItem(size) + HighlightedItem() } Box(modifier = draggingModifier, propagateMinConstraints = true) { content(dragging) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index ff53ff256931..378a1e4858e8 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -34,6 +34,7 @@ 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.map import kotlinx.coroutines.flow.stateIn @@ -43,23 +44,21 @@ class LockscreenScene @Inject constructor( @Application private val applicationScope: CoroutineScope, - private val viewModel: LockscreenSceneViewModel, + viewModel: LockscreenSceneViewModel, private val lockscreenContent: Lazy<LockscreenContent>, ) : ComposableScene { override val key = SceneKey.Lockscreen override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = - viewModel.upDestinationSceneKey - .map { pageKey -> - destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey) - } + combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair) + .map { (upKey, leftKey) -> destinationScenes(up = upKey, left = leftKey) } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, initialValue = destinationScenes( up = viewModel.upDestinationSceneKey.value, - left = viewModel.leftDestinationSceneKey, + left = viewModel.leftDestinationSceneKey.value, ) ) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index b3d2bc994c08..c8fbad4f4eef 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -46,7 +46,7 @@ internal class SceneGestureHandler( val draggable: DraggableHandler = SceneDraggableHandler(this) private var _swipeTransition: SwipeTransition? = null - internal var swipeTransition: SwipeTransition + private var swipeTransition: SwipeTransition get() = _swipeTransition ?: error("SwipeTransition needs to be initialized") set(value) { _swipeTransition = value @@ -92,10 +92,6 @@ internal class SceneGestureHandler( /** The [Swipes] associated to the current gesture. */ private var swipes: Swipes? = null - /** The [UserActionResult] associated to up and down swipes. */ - private var upOrLeftResult: UserActionResult? = null - private var downOrRightResult: UserActionResult? = null - /** * Whether we should immediately intercept a gesture. * @@ -128,7 +124,7 @@ internal class SceneGestureHandler( // This [transition] was already driving the animation: simply take over it. // Stop animating and start from where the current offset. swipeTransition.cancelOffsetAnimation() - updateSwipesResults(swipeTransition._fromScene) + swipes!!.updateSwipesResults(swipeTransition._fromScene) return } @@ -144,16 +140,24 @@ internal class SceneGestureHandler( } val fromScene = layoutImpl.scene(transitionState.currentScene) - updateSwipes(fromScene, startedPosition, pointersDown) - - val result = - findUserActionResult(fromScene, directionOffset = overSlop, updateSwipesResults = true) - ?: return - updateTransition(SwipeTransition(fromScene, result), force = true) - } + val newSwipes = computeSwipes(fromScene, startedPosition, pointersDown) + swipes = newSwipes + val result = newSwipes.findUserActionResult(fromScene, overSlop, true) + + // As we were unable to locate a valid target scene, the initial SwipeTransition cannot be + // defined. + if (result == null) return + + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + swipes = newSwipes, + layoutImpl = layoutImpl, + orientation = orientation + ) - private fun updateSwipes(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) { - this.swipes = computeSwipes(fromScene, startedPosition, pointersDown) + updateTransition(newSwipeTransition, force = true) } private fun computeSwipes( @@ -210,13 +214,6 @@ internal class SceneGestureHandler( } } - private fun Scene.getAbsoluteDistance(distance: UserActionDistance?): Float { - val targetSize = this.targetSize - return with(distance ?: DefaultSwipeDistance) { - layoutImpl.density.absoluteDistance(targetSize, orientation) - } - } - internal fun onDrag(delta: Float) { if (delta == 0f || !isDrivingTransition) return swipeTransition.dragOffset += delta @@ -226,15 +223,17 @@ internal class SceneGestureHandler( val isNewFromScene = fromScene.key != swipeTransition.fromScene val result = - findUserActionResult( - fromScene, - swipeTransition.dragOffset, - updateSwipesResults = isNewFromScene, + swipes!!.findUserActionResult( + fromScene = fromScene, + directionOffset = swipeTransition.dragOffset, + updateSwipesResults = isNewFromScene ) - ?: run { - onDragStopped(delta, true) - return - } + + if (result == null) { + onDragStopped(velocity = delta, canChangeScene = true) + return + } + swipeTransition.dragOffset += acceleratedOffset if ( @@ -242,25 +241,20 @@ internal class SceneGestureHandler( result.toScene != swipeTransition.toScene || result.transitionKey != swipeTransition.key ) { - updateTransition( - SwipeTransition(fromScene, result).apply { - this.dragOffset = swipeTransition.dragOffset - } - ) + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + swipes = swipes!!, + layoutImpl = layoutImpl, + orientation = orientation + ) + .apply { dragOffset = swipeTransition.dragOffset } + + updateTransition(newSwipeTransition) } } - private fun updateSwipesResults(fromScene: Scene) { - val (upOrLeftResult, downOrRightResult) = - computeSwipesResults( - fromScene, - this.swipes ?: error("updateSwipes() should be called before updateSwipesResults()") - ) - - this.upOrLeftResult = upOrLeftResult - this.downOrRightResult = downOrRightResult - } - private fun computeSwipesResults( fromScene: Scene, swipes: Swipes @@ -295,74 +289,20 @@ internal class SceneGestureHandler( // If the swipe was not committed, don't do anything. if (swipeTransition._currentScene != toScene) { - return Pair(fromScene, 0f) + return fromScene to 0f } // If the offset is past the distance then let's change fromScene so that the user can swipe // to the next screen or go back to the previous one. val offset = swipeTransition.dragOffset - return if (offset <= -absoluteDistance && upOrLeftResult?.toScene == toScene.key) { - Pair(toScene, absoluteDistance) - } else if (offset >= absoluteDistance && downOrRightResult?.toScene == toScene.key) { - Pair(toScene, -absoluteDistance) - } else { - Pair(fromScene, 0f) - } - } - - /** - * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset]. - * - * @param fromScene the scene from which we look for the target - * @param directionOffset signed float that indicates the direction. Positive is down or right - * negative is up or left. - * @param updateSwipesResults whether the target scenes should be updated to the current values - * held in the Scenes map. Usually we don't want to update them while doing a drag, because - * this could change the target scene (jump cutting) to a different scene, when some system - * state changed the targets the background. However, an update is needed any time we - * calculate the targets for a new fromScene. - * @return null when there are no targets in either direction. If one direction is null and you - * drag into the null direction this function will return the opposite direction, assuming - * that the users intention is to start the drag into the other direction eventually. If - * [directionOffset] is 0f and both direction are available, it will default to - * [upOrLeftResult]. - */ - private fun findUserActionResult( - fromScene: Scene, - directionOffset: Float, - updateSwipesResults: Boolean, - ): UserActionResult? { - if (updateSwipesResults) updateSwipesResults(fromScene) - - return when { - upOrLeftResult == null && downOrRightResult == null -> null - (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> - upOrLeftResult - else -> downOrRightResult - } - } - - /** - * A strict version of [findUserActionResult] that will return null when there is no Scene in - * [directionOffset] direction - */ - private fun findUserActionResultStrict(directionOffset: Float): UserActionResult? { - return when { - directionOffset > 0f -> upOrLeftResult - directionOffset < 0f -> downOrRightResult - else -> null - } - } - - private fun computeAbsoluteDistance( - fromScene: Scene, - result: UserActionResult, - ): Float { - return if (result == upOrLeftResult) { - -fromScene.getAbsoluteDistance(result.distance) + return if (offset <= -absoluteDistance && swipes!!.upOrLeftResult?.toScene == toScene.key) { + toScene to absoluteDistance + } else if ( + offset >= absoluteDistance && swipes!!.downOrRightResult?.toScene == toScene.key + ) { + toScene to -absoluteDistance } else { - check(result == downOrRightResult) - fromScene.getAbsoluteDistance(result.distance) + fromScene to 0f } } @@ -430,19 +370,24 @@ internal class SceneGestureHandler( if (startFromIdlePosition) { // If there is a target scene, we start the overscroll animation. - val result = - findUserActionResultStrict(velocity) - ?: run { - // We will not animate - layoutState.finishTransition(swipeTransition, idleScene = fromScene.key) - return - } + val result = swipes!!.findUserActionResultStrict(velocity) + if (result == null) { + // We will not animate + layoutState.finishTransition(swipeTransition, idleScene = fromScene.key) + return + } - updateTransition( - SwipeTransition(fromScene, result).apply { - _currentScene = swipeTransition._currentScene - } - ) + val newSwipeTransition = + SwipeTransition( + fromScene = fromScene, + result = result, + swipes = swipes!!, + layoutImpl = layoutImpl, + orientation = orientation + ) + .apply { _currentScene = swipeTransition._currentScene } + + updateTransition(newSwipeTransition) animateTo(targetScene = fromScene, targetOffset = 0f) } else { // We were between two scenes: animate to the initial scene. @@ -486,134 +431,220 @@ internal class SceneGestureHandler( } } - private fun SwipeTransition(fromScene: Scene, result: UserActionResult): SwipeTransition { - return SwipeTransition( - result.transitionKey, - fromScene, - layoutImpl.scene(result.toScene), - computeAbsoluteDistance(fromScene, result), - ) + companion object { + private const val TAG = "SceneGestureHandler" } +} - internal class SwipeTransition( - val key: TransitionKey?, - val _fromScene: Scene, - val _toScene: Scene, - /** - * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is - * above or to the left of [toScene]. - */ - val distance: Float, - ) : TransitionState.Transition(_fromScene.key, _toScene.key) { - var _currentScene by mutableStateOf(_fromScene) - override val currentScene: SceneKey - get() = _currentScene.key - - override val progress: Float - get() { - val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset - return offset / distance - } +private fun SwipeTransition( + fromScene: Scene, + result: UserActionResult, + swipes: Swipes, + layoutImpl: SceneTransitionLayoutImpl, + orientation: Orientation, +): SwipeTransition { + val upOrLeftResult = swipes.upOrLeftResult + val downOrRightResult = swipes.downOrRightResult + val userActionDistance = result.distance ?: DefaultSwipeDistance + val absoluteDistance = + with(userActionDistance) { + layoutImpl.density.absoluteDistance(fromScene.targetSize, orientation) + } + + return SwipeTransition( + key = result.transitionKey, + _fromScene = fromScene, + _toScene = layoutImpl.scene(result.toScene), + distance = + when (result) { + upOrLeftResult -> -absoluteDistance + downOrRightResult -> absoluteDistance + else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)") + }, + ) +} + +private class SwipeTransition( + val key: TransitionKey?, + val _fromScene: Scene, + val _toScene: Scene, + /** + * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above + * or to the left of [toScene] + */ + val distance: Float, +) : TransitionState.Transition(_fromScene.key, _toScene.key) { + var _currentScene by mutableStateOf(_fromScene) + override val currentScene: SceneKey + get() = _currentScene.key + + override val progress: Float + get() { + val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset + return offset / distance + } - override val isInitiatedByUserInput = true + override val isInitiatedByUserInput = true - /** The current offset caused by the drag gesture. */ - var dragOffset by mutableFloatStateOf(0f) + /** The current offset caused by the drag gesture. */ + var dragOffset by mutableFloatStateOf(0f) - /** - * Whether the offset is animated (the user lifted their finger) or if it is driven by - * gesture. - */ - var isAnimatingOffset by mutableStateOf(false) + /** + * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture. + */ + var isAnimatingOffset by mutableStateOf(false) - // If we are not animating offset, it means the offset is being driven by the user's finger. - override val isUserInputOngoing: Boolean - get() = !isAnimatingOffset + // If we are not animating offset, it means the offset is being driven by the user's finger. + override val isUserInputOngoing: Boolean + get() = !isAnimatingOffset - /** The animatable used to animate the offset once the user lifted its finger. */ - val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) + /** The animatable used to animate the offset once the user lifted its finger. */ + val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) - /** Job to check that there is at most one offset animation in progress. */ - private var offsetAnimationJob: Job? = null + /** Job to check that there is at most one offset animation in progress. */ + private var offsetAnimationJob: Job? = null - /** The spec to use when animating this transition to either [fromScene] or [toScene]. */ - lateinit var swipeSpec: SpringSpec<Float> + /** The spec to use when animating this transition to either [fromScene] or [toScene]. */ + lateinit var swipeSpec: SpringSpec<Float> - /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - private fun startOffsetAnimation(job: () -> Job) { - cancelOffsetAnimation() - offsetAnimationJob = job() - } + /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ + private fun startOffsetAnimation(job: () -> Job) { + cancelOffsetAnimation() + offsetAnimationJob = job() + } + + /** Cancel any ongoing offset animation. */ + // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at + // the same time. + fun cancelOffsetAnimation() { + offsetAnimationJob?.cancel() + finishOffsetAnimation() + } - /** Cancel any ongoing offset animation. */ - // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at - // the same time. - fun cancelOffsetAnimation() { - offsetAnimationJob?.cancel() - finishOffsetAnimation() + fun finishOffsetAnimation() { + if (isAnimatingOffset) { + isAnimatingOffset = false + dragOffset = offsetAnimatable.value } + } - fun finishOffsetAnimation() { - if (isAnimatingOffset) { - isAnimatingOffset = false - dragOffset = offsetAnimatable.value + fun animateOffset( + // TODO(b/317063114) The CoroutineScope should be removed. + coroutineScope: CoroutineScope, + initialVelocity: Float, + targetOffset: Float, + onAnimationCompleted: () -> Unit, + ) { + startOffsetAnimation { + coroutineScope.launch { + animateOffset(targetOffset, initialVelocity) + onAnimationCompleted() } } + } - fun animateOffset( - // TODO(b/317063114) The CoroutineScope should be removed. - coroutineScope: CoroutineScope, - initialVelocity: Float, - targetOffset: Float, - onAnimationCompleted: () -> Unit, - ) { - startOffsetAnimation { - coroutineScope.launch { - animateOffset(targetOffset, initialVelocity) - onAnimationCompleted() - } - } + private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { + if (!isAnimatingOffset) { + offsetAnimatable.snapTo(dragOffset) } + isAnimatingOffset = true - private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { - if (!isAnimatingOffset) { - offsetAnimatable.snapTo(dragOffset) - } - isAnimatingOffset = true + offsetAnimatable.animateTo( + targetValue = targetOffset, + animationSpec = swipeSpec, + initialVelocity = initialVelocity, + ) - offsetAnimatable.animateTo( - targetValue = targetOffset, - animationSpec = swipeSpec, - initialVelocity = initialVelocity, - ) + finishOffsetAnimation() + } +} + +private object DefaultSwipeDistance : UserActionDistance { + override fun Density.absoluteDistance( + fromSceneSize: IntSize, + orientation: Orientation, + ): Float { + return when (orientation) { + Orientation.Horizontal -> fromSceneSize.width + Orientation.Vertical -> fromSceneSize.height + }.toFloat() + } +} - finishOffsetAnimation() +/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */ +private class Swipes( + val upOrLeft: Swipe?, + val downOrRight: Swipe?, + val upOrLeftNoSource: Swipe?, + val downOrRightNoSource: Swipe?, +) { + /** The [UserActionResult] associated to up and down swipes. */ + var upOrLeftResult: UserActionResult? = null + var downOrRightResult: UserActionResult? = null + + fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> { + val userActions = fromScene.userActions + fun result(swipe: Swipe?): UserActionResult? { + return userActions[swipe ?: return null] } + + val upOrLeftResult = result(upOrLeft) ?: result(upOrLeftNoSource) + val downOrRightResult = result(downOrRight) ?: result(downOrRightNoSource) + return upOrLeftResult to downOrRightResult } - companion object { - private const val TAG = "SceneGestureHandler" + fun updateSwipesResults(fromScene: Scene) { + val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromScene) + + this.upOrLeftResult = upOrLeftResult + this.downOrRightResult = downOrRightResult } - private object DefaultSwipeDistance : UserActionDistance { - override fun Density.absoluteDistance( - fromSceneSize: IntSize, - orientation: Orientation, - ): Float { - return when (orientation) { - Orientation.Horizontal -> fromSceneSize.width - Orientation.Vertical -> fromSceneSize.height - }.toFloat() + /** + * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset]. + * + * @param fromScene the scene from which we look for the target + * @param directionOffset signed float that indicates the direction. Positive is down or right + * negative is up or left. + * @param updateSwipesResults whether the target scenes should be updated to the current values + * held in the Scenes map. Usually we don't want to update them while doing a drag, because + * this could change the target scene (jump cutting) to a different scene, when some system + * state changed the targets the background. However, an update is needed any time we + * calculate the targets for a new fromScene. + * @return null when there are no targets in either direction. If one direction is null and you + * drag into the null direction this function will return the opposite direction, assuming + * that the users intention is to start the drag into the other direction eventually. If + * [directionOffset] is 0f and both direction are available, it will default to + * [upOrLeftResult]. + */ + fun findUserActionResult( + fromScene: Scene, + directionOffset: Float, + updateSwipesResults: Boolean, + ): UserActionResult? { + if (updateSwipesResults) { + updateSwipesResults(fromScene) + } + + return when { + upOrLeftResult == null && downOrRightResult == null -> null + (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> + upOrLeftResult + else -> downOrRightResult } } - /** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */ - private class Swipes( - val upOrLeft: Swipe?, - val downOrRight: Swipe?, - val upOrLeftNoSource: Swipe?, - val downOrRightNoSource: Swipe?, - ) + /** + * A strict version of [findUserActionResult] that will return null when there is no Scene in + * [directionOffset] direction + */ + fun findUserActionResultStrict(directionOffset: Float): UserActionResult? { + return when { + directionOffset > 0f -> upOrLeftResult + directionOffset < 0f -> downOrRightResult + else -> null + } + } } private class SceneDraggableHandler( diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index dacbdb484d0c..c91d29880ffb 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -127,6 +127,9 @@ class SceneGestureHandlerTest { val progress: Float get() = (transitionState as Transition).progress + val isUserInputOngoing: Boolean + get() = (transitionState as Transition).isUserInputOngoing + fun advanceUntilIdle() { testScope.testScheduler.advanceUntilIdle() } @@ -538,12 +541,11 @@ class SceneGestureHandlerTest { onDragStopped(velocity = velocityThreshold) assertTransition(currentScene = SceneC) - assertThat(sceneGestureHandler.isDrivingTransition).isTrue() - assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isTrue() + assertThat(isUserInputOngoing).isFalse() // Start a new gesture while the offset is animating onDragStartedImmediately() - assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse() + assertThat(isUserInputOngoing).isTrue() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index f7743e2814f0..259f3498d4c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -37,7 +37,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel @@ -106,7 +106,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsController: UdfpsController @Mock private lateinit var udfpsView: UdfpsView @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy - @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator + @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor @@ -167,7 +167,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { reason, controllerCallback, onTouch, - activityLaunchAnimator, + mActivityTransitionAnimator, primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 90c3c14bbc4f..529403a710b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -75,7 +75,7 @@ import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; import com.android.systemui.biometrics.udfps.InteractionEvent; @@ -203,7 +203,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private SystemUIDialogManager mSystemUIDialogManager; @Mock - private ActivityLaunchAnimator mActivityLaunchAnimator; + private ActivityTransitionAnimator mActivityTransitionAnimator; @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Mock @@ -331,7 +331,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker, - mActivityLaunchAnimator, + mActivityTransitionAnimator, mBiometricExecutor, mPrimaryBouncerInteractor, mShadeInteractor, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java index 7d9c2f96ef58..324534fb766c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java @@ -26,7 +26,7 @@ import android.content.Context; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; @@ -70,7 +70,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; protected @Mock SystemUIDialogManager mDialogManager; protected @Mock UdfpsController mUdfpsController; - protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator; + protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator; protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; protected @Mock ShadeInteractor mShadeInteractor; protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; @@ -148,7 +148,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { mUnlockedScreenOffAnimationController, mDialogManager, mUdfpsController, - mActivityLaunchAnimator, + mActivityTransitionAnimator, mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsKeyguardAccessibilityDelegate, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt index bd9ca3035a07..b4e2eab22c88 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt @@ -16,26 +16,18 @@ package com.android.systemui.communal.data.repository -import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.Flags -import com.android.systemui.flags.fakeFeatureFlagsClassic -import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.testKosmos -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.data.repository.fakeUserRepository -import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest @@ -48,37 +40,20 @@ import org.junit.runner.RunWith class CommunalRepositoryImplTest : SysuiTestCase() { private lateinit var underTest: CommunalRepositoryImpl - private lateinit var secureSettings: FakeSettings - private lateinit var userRepository: FakeUserRepository - private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneContainerRepository = kosmos.sceneContainerRepository @Before fun setUp() { - secureSettings = FakeSettings() - userRepository = kosmos.fakeUserRepository - - val listOfUserInfo = listOf(MAIN_USER_INFO) - userRepository.setUserInfos(listOfUserInfo) - - kosmos.fakeFeatureFlagsClassic.apply { set(Flags.COMMUNAL_SERVICE_ENABLED, true) } - mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) - underTest = createRepositoryImpl(false) } private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl { return CommunalRepositoryImpl( testScope.backgroundScope, - testScope.backgroundScope, - kosmos.testDispatcher, - kosmos.fakeFeatureFlagsClassic, kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled }, sceneContainerRepository, - kosmos.fakeUserRepository, - secureSettings, ) } @@ -159,29 +134,4 @@ class CommunalRepositoryImplTest : SysuiTestCase() { assertThat(transitionState) .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)) } - - @Test - fun communalEnabledState_false_whenGlanceableHubSettingFalse() = - testScope.runTest { - userRepository.setSelectedUserInfo(MAIN_USER_INFO) - secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, MAIN_USER_INFO.id) - - val communalEnabled by collectLastValue(underTest.communalEnabledState) - assertThat(communalEnabled).isFalse() - } - - @Test - fun communalEnabledState_true_whenGlanceableHubSettingTrue() = - testScope.runTest { - userRepository.setSelectedUserInfo(MAIN_USER_INFO) - secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, MAIN_USER_INFO.id) - - val communalEnabled by collectLastValue(underTest.communalEnabledState) - assertThat(communalEnabled).isTrue() - } - - companion object { - private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled" - private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt new file mode 100644 index 000000000000..0aca16d9aeaa --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.data.repository + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL +import android.app.admin.devicePolicyManager +import android.content.Intent +import android.content.pm.UserInfo +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.broadcastDispatcher +import com.android.systemui.communal.data.model.DisabledReason +import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED +import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.settings.fakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CommunalSettingsRepositoryImplTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private lateinit var underTest: CommunalSettingsRepository + + @Before + fun setUp() { + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) + setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE) + setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE) + underTest = kosmos.communalSettingsRepository + } + + @EnableFlags(FLAG_COMMUNAL_HUB) + @Test + fun secondaryUserIsInvalid() = + testScope.runTest { + val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER)) + + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER) + } + + @EnableFlags(FLAG_COMMUNAL_HUB) + @Test + fun classicFlagIsDisabled() = + testScope.runTest { + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false) + val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG) + } + + @DisableFlags(FLAG_COMMUNAL_HUB) + @Test + fun communalHubFlagIsDisabled() = + testScope.runTest { + val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG) + } + + @EnableFlags(FLAG_COMMUNAL_HUB) + @Test + fun hubIsDisabledByUser() = + testScope.runTest { + kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id) + val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING) + + kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id) + assertThat(enabledState?.enabled).isFalse() + + kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id) + assertThat(enabledState?.enabled).isTrue() + } + + @EnableFlags(FLAG_COMMUNAL_HUB) + @Test + fun hubIsDisabledByDevicePolicy() = + testScope.runTest { + val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) + assertThat(enabledState?.enabled).isTrue() + + setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL) + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY) + } + + @EnableFlags(FLAG_COMMUNAL_HUB) + @Test + fun hubIsDisabledByUserAndDevicePolicy() = + testScope.runTest { + val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER)) + assertThat(enabledState?.enabled).isTrue() + + kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id) + setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL) + + assertThat(enabledState?.enabled).isFalse() + assertThat(enabledState) + .containsExactly( + DisabledReason.DISABLED_REASON_DEVICE_POLICY, + DisabledReason.DISABLED_REASON_USER_SETTING, + ) + } + + private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) { + whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id))) + .thenReturn(disabledFlags) + kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), + ) + } + + private companion object { + val PRIMARY_USER = + UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN) + val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt index 475179dfdb72..a54a00d539b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt @@ -52,6 +52,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -198,23 +199,27 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { } @Test - fun deleteWidgetFromDb() = + fun deleteWidget_deletefromDbTrue_alsoDeleteFromHost() = testScope.runTest { val id = 1 - underTest.deleteWidgetFromDb(id) + whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(true) + underTest.deleteWidget(id) runCurrent() verify(communalWidgetDao).deleteWidgetById(id) + verify(appWidgetHost).deleteAppWidgetId(id) } @Test - fun deleteWidgetFromHost() = + fun deleteWidget_deletefromDbFalse_doesNotDeleteFromHost() = testScope.runTest { val id = 1 - underTest.deleteWidgetFromHost(id) + whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(false) + underTest.deleteWidget(id) runCurrent() - verify(appWidgetHost).deleteAppWidgetId(id) + verify(communalWidgetDao).deleteWidgetById(id) + verify(appWidgetHost, never()).deleteAppWidgetId(id) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt index 6a3fc2a060eb..824733bcc47b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.communal.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository @@ -59,7 +60,7 @@ class CommunalInteractorCommunalDisabledTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository keyguardRepository = kosmos.fakeKeyguardRepository - communalRepository.setIsCommunalEnabled(false) + mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB) underTest = kosmos.communalInteractor } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index c5485c5c5c33..3ac19e4672c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -23,6 +23,7 @@ import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository @@ -41,6 +42,8 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope @@ -109,12 +112,19 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(secondaryUser.isMain).thenReturn(false) userRepository.setUserInfos(listOf(mainUser, secondaryUser)) + kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) + mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) + underTest = kosmos.communalInteractor } @Test fun communalEnabled_true() = - testScope.runTest { assertThat(underTest.isCommunalEnabled).isTrue() } + testScope.runTest { + userRepository.setSelectedUserInfo(mainUser) + runCurrent() + assertThat(underTest.isCommunalEnabled).isTrue() + } @Test fun isCommunalAvailable_storageUnlockedAndMainUser_true() = @@ -125,7 +135,6 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setKeyguardShowing(true) - communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isTrue() } @@ -139,7 +148,6 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(true) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setKeyguardShowing(true) - communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isFalse() } @@ -153,7 +161,6 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(secondaryUser) keyguardRepository.setKeyguardShowing(true) - communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isFalse() } @@ -167,7 +174,6 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setDreaming(true) - communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isTrue() } @@ -175,13 +181,14 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun isCommunalAvailable_communalDisabled_false() = testScope.runTest { + mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB) + val isAvailable by collectLastValue(underTest.isCommunalAvailable) assertThat(isAvailable).isFalse() keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setKeyguardShowing(true) - communalRepository.setCommunalEnabledState(false) assertThat(isAvailable).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt index 6c87e0f2eb23..ceb7fac1046f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -22,12 +22,15 @@ import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope @@ -35,11 +38,13 @@ import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class CommunalTutorialInteractorTest : SysuiTestCase() { @@ -62,6 +67,8 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { userRepository = kosmos.fakeUserRepository userRepository.setUserInfos(listOf(MAIN_USER_INFO)) + kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) + mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) underTest = kosmos.communalTutorialInteractor } @@ -127,6 +134,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) communalRepository.setIsCommunalHubShowing(true) @@ -139,6 +147,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) communalRepository.setIsCommunalHubShowing(true) @@ -151,6 +160,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) communalRepository.setIsCommunalHubShowing(true) @@ -163,6 +173,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) communalRepository.setIsCommunalHubShowing(false) @@ -175,6 +186,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalRepository.setIsCommunalHubShowing(true) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) @@ -188,6 +200,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { testScope.runTest { val tutorialSettingState by collectLastValue(communalTutorialRepository.tutorialSettingState) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) communalRepository.setIsCommunalHubShowing(true) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) @@ -198,14 +211,11 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { private suspend fun setCommunalAvailable(available: Boolean) { if (available) { - communalRepository.setIsCommunalEnabled(true) - communalRepository.setCommunalEnabledState(true) keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(MAIN_USER_INFO) keyguardRepository.setKeyguardShowing(true) } else { - communalRepository.setIsCommunalEnabled(false) - communalRepository.setCommunalEnabledState(false) + keyguardRepository.setIsEncryptedOrLockdown(true) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 73d309118548..f70b6a5a170f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -22,12 +22,12 @@ import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository -import com.android.systemui.communal.data.repository.fakeCommunalRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.communalInteractor @@ -37,6 +37,8 @@ import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope @@ -92,7 +94,8 @@ class CommunalViewModelTest : SysuiTestCase() { mediaRepository = kosmos.fakeCommunalMediaRepository userRepository = kosmos.fakeUserRepository - kosmos.fakeCommunalRepository.setCommunalEnabledState(true) + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) + mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) underTest = CommunalViewModel( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt index 032d76f0dceb..8488843905f7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt @@ -19,12 +19,15 @@ package com.android.systemui.communal.widgets import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase -import com.android.systemui.communal.data.repository.fakeCommunalRepository +import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher @@ -33,6 +36,7 @@ import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.settings.fakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow @@ -62,6 +66,8 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) + kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) + mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) appWidgetIdToRemove = MutableSharedFlow() whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove) @@ -169,7 +175,8 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { fakeKeyguardRepository.setIsEncryptedOrLockdown(false) fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO) fakeKeyguardRepository.setKeyguardShowing(true) - fakeCommunalRepository.setCommunalEnabledState(available) + val settingsValue = if (available) 1 else 0 + fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id) } private companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java index 2a9bc4a1d80e..8dcf9032759b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java @@ -18,15 +18,20 @@ package com.android.systemui.dreams.touch; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.view.GestureDetector; import android.view.MotionEvent; +import androidx.lifecycle.Lifecycle; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -39,28 +44,60 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; @SmallTest @RunWith(AndroidJUnit4.class) public class CommunalTouchHandlerTest extends SysuiTestCase { + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + @Mock CentralSurfaces mCentralSurfaces; @Mock DreamTouchHandler.TouchSession mTouchSession; CommunalTouchHandler mTouchHandler; + @Mock + Lifecycle mLifecycle; private static final int INITIATION_WIDTH = 20; @Before public void setup() { MockitoAnnotations.initMocks(this); + AtomicReference reference = new AtomicReference<>(null); + when(mLifecycle.getInternalScopeRef()).thenReturn(reference); + when(mLifecycle.getCurrentState()).thenReturn(Lifecycle.State.CREATED); mTouchHandler = new CommunalTouchHandler( Optional.of(mCentralSurfaces), - INITIATION_WIDTH); + INITIATION_WIDTH, + mKosmos.getCommunalInteractor(), + mLifecycle + ); + } + + @Test + public void communalTouchHandler_disabledByDefault() { + assertThat(mTouchHandler.isEnabled()).isFalse(); + } + + @Test + public void communalTouchHandler_disabled_whenCommunalUnavailable() { + mTouchHandler.mIsCommunalAvailableCallback.accept(false); + assertThat(mTouchHandler.isEnabled()).isFalse(); + + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession, never()).registerGestureListener(any()); + } + + @Test + public void communalTouchHandler_enabled_whenCommunalAvailable() { + mTouchHandler.mIsCommunalAvailableCallback.accept(true); + assertThat(mTouchHandler.isEnabled()).isTrue(); } @Test public void testEventPropagation() { + mTouchHandler.mIsCommunalAvailableCallback.accept(true); final MotionEvent motionEvent = Mockito.mock(MotionEvent.class); final ArgumentCaptor<InputChannelCompat.InputEventListener> @@ -75,6 +112,7 @@ public class CommunalTouchHandlerTest extends SysuiTestCase { @Test public void testTouchPilferingOnScroll() { + mTouchHandler.mIsCommunalAvailableCallback.accept(true); final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class); final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index a613ad8852f6..076834097717 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -23,14 +23,14 @@ import android.service.quickaccesswallet.QuickAccessWalletClient import android.service.quickaccesswallet.WalletCard import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -76,26 +76,28 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_keyguardShowing_hasWalletCard_visibleModel() = testScope.runTest { - setUpState() - - val latest by collectLastValue(underTest.lockScreenState) - - val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible - assertThat(visibleModel.icon) - .isEqualTo( - Icon.Loaded( - drawable = ICON, - contentDescription = - ContentDescription.Resource( - res = R.string.accessibility_wallet_button, - ), + fun affordance_keyguardShowing_hasWalletCard_visibleModel() = + testScope.runTest { + setUpState() + + val latest by collectLastValue(underTest.lockScreenState) + + val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible + assertThat(visibleModel.icon) + .isEqualTo( + Icon.Loaded( + drawable = ICON, + contentDescription = + ContentDescription.Resource( + res = R.string.accessibility_wallet_button, + ), + ) ) - ) - } + } @Test - fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = testScope.runTest { + fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = + testScope.runTest { setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT) val latest by collectLastValue(underTest.lockScreenState) @@ -104,54 +106,58 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = testScope.runTest { - setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT) - - val latest by collectLastValue(underTest.lockScreenState) - - val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible - assertThat(visibleModel.icon) - .isEqualTo( - Icon.Loaded( - drawable = ICON, - contentDescription = - ContentDescription.Resource( - res = R.string.accessibility_wallet_button, - ), + fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = + testScope.runTest { + setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT) + + val latest by collectLastValue(underTest.lockScreenState) + + val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible + assertThat(visibleModel.icon) + .isEqualTo( + Icon.Loaded( + drawable = ICON, + contentDescription = + ContentDescription.Resource( + res = R.string.accessibility_wallet_button, + ), + ) ) - ) - } + } @Test - fun affordance_walletFeatureNotEnabled_modelIsNone() = testScope.runTest { - setUpState(isWalletFeatureAvailable = false) + fun affordance_walletFeatureNotEnabled_modelIsNone() = + testScope.runTest { + setUpState(isWalletFeatureAvailable = false) - val latest by collectLastValue(underTest.lockScreenState) + val latest by collectLastValue(underTest.lockScreenState) - assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) - } + assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) + } @Test - fun affordance_queryNotSuccessful_modelIsNone() = testScope.runTest { - setUpState(isWalletQuerySuccessful = false) + fun affordance_queryNotSuccessful_modelIsNone() = + testScope.runTest { + setUpState(isWalletQuerySuccessful = false) - val latest by collectLastValue(underTest.lockScreenState) + val latest by collectLastValue(underTest.lockScreenState) - assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) - } + assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) + } @Test - fun affordance_noSelectedCard_modelIsNone() = testScope.runTest { - setUpState(hasSelectedCard = false) + fun affordance_noSelectedCard_modelIsNone() = + testScope.runTest { + setUpState(hasSelectedCard = false) - val latest by collectLastValue(underTest.lockScreenState) + val latest by collectLastValue(underTest.lockScreenState) - assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) - } + assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) + } @Test fun onQuickAffordanceTriggered() { - val animationController: ActivityLaunchAnimator.Controller = mock() + val animationController: ActivityTransitionAnimator.Controller = mock() val expandable: Expandable = mock { whenever(this.activityLaunchController()).thenReturn(animationController) } @@ -167,42 +173,46 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun getPickerScreenState_default() = testScope.runTest { - setUpState() + fun getPickerScreenState_default() = + testScope.runTest { + setUpState() - assertThat(underTest.getPickerScreenState()) - .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) - } + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) + } @Test - fun getPickerScreenState_unavailable() = testScope.runTest { - setUpState( - isWalletServiceAvailable = false, - ) + fun getPickerScreenState_unavailable() = + testScope.runTest { + setUpState( + isWalletServiceAvailable = false, + ) - assertThat(underTest.getPickerScreenState()) - .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) - } + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) + } @Test - fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = testScope.runTest { - setUpState( - isWalletFeatureAvailable = false, - ) + fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = + testScope.runTest { + setUpState( + isWalletFeatureAvailable = false, + ) - assertThat(underTest.getPickerScreenState()) - .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) - } + assertThat(underTest.getPickerScreenState()) + .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) + } @Test - fun getPickerScreenState_disabledWhenThereIsNoCard() = testScope.runTest { - setUpState( - hasSelectedCard = false, - ) + fun getPickerScreenState_disabledWhenThereIsNoCard() = + testScope.runTest { + setUpState( + hasSelectedCard = false, + ) - assertThat(underTest.getPickerScreenState()) - .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) - } + assertThat(underTest.getPickerScreenState()) + .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) + } private fun setUpState( isWalletFeatureAvailable: Boolean = true, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 4595fbfab0d7..72617238cbb1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -18,22 +18,28 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.content.pm.UserInfo +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.communal.data.repository.fakeCommunalRepository -import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -49,9 +55,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } - private val underTest by lazy { - createLockscreenSceneViewModel() - } + private val underTest by lazy { createLockscreenSceneViewModel() } @Test fun upTransitionSceneKey_canSwipeToUnlock_gone() = @@ -80,29 +84,37 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer) } + @EnableFlags(FLAG_COMMUNAL_HUB) @Test fun leftTransitionSceneKey_communalIsEnabled_communal() = testScope.runTest { - kosmos.fakeCommunalRepository.setIsCommunalEnabled(true) - val underTest = createLockscreenSceneViewModel() - - assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal) + with(kosmos.fakeUserRepository) { + setUserInfos(listOf(PRIMARY_USER)) + setSelectedUserInfo(PRIMARY_USER) + } + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) + val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey) + assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal) } + @DisableFlags(FLAG_COMMUNAL_HUB) @Test fun leftTransitionSceneKey_communalIsDisabled_null() = testScope.runTest { - kosmos.fakeCommunalRepository.setIsCommunalEnabled(false) - val underTest = createLockscreenSceneViewModel() - - assertThat(underTest.leftDestinationSceneKey).isNull() + with(kosmos.fakeUserRepository) { + setUserInfos(listOf(PRIMARY_USER)) + setSelectedUserInfo(PRIMARY_USER) + } + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false) + val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey) + assertThat(leftDestinationSceneKey).isNull() } private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel { return LockscreenSceneViewModel( applicationScope = testScope.backgroundScope, deviceEntryInteractor = kosmos.deviceEntryInteractor, - communalInteractor = kosmos.communalInteractor, + communalSettingsInteractor = kosmos.communalSettingsInteractor, longPress = KeyguardLongPressViewModel( interactor = mock(), @@ -110,4 +122,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { notifications = kosmos.notificationsPlaceholderViewModel, ) } + + private companion object { + val PRIMARY_USER = + UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 9d3f0d6a93f8..006f429ab98a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -40,7 +40,7 @@ import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel import com.android.systemui.classifier.domain.interactor.falsingInteractor import com.android.systemui.classifier.falsingCollector -import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -130,7 +130,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val sceneInteractor by lazy { kosmos.sceneInteractor } private val authenticationInteractor by lazy { kosmos.authenticationInteractor } private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor } - private val communalInteractor by lazy { kosmos.communalInteractor } + private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor } private val transitionState by lazy { MutableStateFlow<ObservableTransitionState>( @@ -155,7 +155,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { LockscreenSceneViewModel( applicationScope = testScope.backgroundScope, deviceEntryInteractor = deviceEntryInteractor, - communalInteractor = communalInteractor, + communalSettingsInteractor = communalSettingsInteractor, longPress = KeyguardLongPressViewModel( interactor = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt index 2951fc0232b3..43409718c849 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt @@ -18,12 +18,12 @@ package com.android.systemui.statusbar.events import android.graphics.Point import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.DisplayAdjustments import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.FakeStatusBarStateController @@ -44,11 +44,12 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class PrivacyDotViewControllerTest : SysuiTestCase() { - private val context = getContext().createDisplayContext(createMockDisplay()) + private val mockDisplay = createMockDisplay() + private val context = getContext().createDisplayContext(mockDisplay) private val testScope = TestScope() private val executor = InstantExecutor() @@ -61,7 +62,12 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { private val bottomLeftView = initDotView() private val bottomRightView = initDotView() - private val controller = + private fun createAndInitializeController() = + createController().also { + it.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + } + + private fun createController() = PrivacyDotViewController( executor, testScope.backgroundScope, @@ -71,14 +77,11 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { animationScheduler = mock<SystemStatusAnimationScheduler>(), shadeInteractor = null ) - .also { - it.setUiExecutor(executor) - it.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) - } + .also { it.setUiExecutor(executor) } @Test fun topMargin_topLeftView_basedOnSeascapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topLeftView.frameLayoutParams.topMargin) .isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.top) @@ -86,7 +89,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun topMargin_topRightView_basedOnPortraitArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topRightView.frameLayoutParams.topMargin) .isEqualTo(CONTENT_AREA_ROTATION_NONE.top) @@ -94,7 +97,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun topMargin_bottomLeftView_basedOnUpsideDownArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomLeftView.frameLayoutParams.topMargin) .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.top) @@ -102,7 +105,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun topMargin_bottomRightView_basedOnLandscapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomRightView.frameLayoutParams.topMargin) .isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.top) @@ -110,7 +113,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun height_topLeftView_basedOnSeascapeAreaHeight() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topLeftView.layoutParams.height) .isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.height()) @@ -118,14 +121,14 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun height_topRightView_basedOnPortraitAreaHeight() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topRightView.layoutParams.height).isEqualTo(CONTENT_AREA_ROTATION_NONE.height()) } @Test fun height_bottomLeftView_basedOnUpsidedownAreaHeight() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomLeftView.layoutParams.height) .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.height()) @@ -133,7 +136,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun height_bottomRightView_basedOnLandscapeAreaHeight() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomRightView.layoutParams.height) .isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.height()) @@ -141,7 +144,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_topLeftView_ltr_basedOnDisplayHeightAndSeascapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topLeftView.layoutParams.width) .isEqualTo(DISPLAY_HEIGHT - CONTENT_AREA_ROTATION_SEASCAPE.right) @@ -149,15 +152,15 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_topLeftView_rtl_basedOnPortraitArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) - configurationController.notifyLayoutDirectionChanged(isRtl = true) + createAndInitializeController() + enableRtl() assertThat(topLeftView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_NONE.left) } @Test fun width_topRightView_ltr_basedOnPortraitArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(topRightView.layoutParams.width) .isEqualTo(DISPLAY_WIDTH - CONTENT_AREA_ROTATION_NONE.right) @@ -165,15 +168,15 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_topRightView_rtl_basedOnLandscapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) - configurationController.notifyLayoutDirectionChanged(isRtl = true) + createAndInitializeController() + enableRtl() assertThat(topRightView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_LANDSCAPE.left) } @Test fun width_bottomRightView_ltr_basedOnDisplayHeightAndLandscapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomRightView.layoutParams.width) .isEqualTo(DISPLAY_HEIGHT - CONTENT_AREA_ROTATION_LANDSCAPE.right) @@ -181,8 +184,8 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_bottomRightView_rtl_basedOnUpsideDown() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) - configurationController.notifyLayoutDirectionChanged(isRtl = true) + createAndInitializeController() + enableRtl() assertThat(bottomRightView.layoutParams.width) .isEqualTo(CONTENT_AREA_ROTATION_UPSIDE_DOWN.left) @@ -190,7 +193,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_bottomLeftView_ltr_basedOnDisplayWidthAndUpsideDownArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) + createAndInitializeController() assertThat(bottomLeftView.layoutParams.width) .isEqualTo(DISPLAY_WIDTH - CONTENT_AREA_ROTATION_UPSIDE_DOWN.right) @@ -198,16 +201,108 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { @Test fun width_bottomLeftView_rtl_basedOnSeascapeArea() { - controller.initialize(topLeftView, topRightView, bottomLeftView, bottomRightView) - configurationController.notifyLayoutDirectionChanged(isRtl = true) + createAndInitializeController() + enableRtl() assertThat(bottomLeftView.layoutParams.width).isEqualTo(CONTENT_AREA_ROTATION_SEASCAPE.left) } + @Test + fun initialize_rotationPortrait_activeCornerIsTopRight() { + setRotation(ROTATION_NONE) + + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView) + } + + @Test + fun initialize_rotationLandscape_activeCornerIsBottomRight() { + setRotation(ROTATION_LANDSCAPE) + + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView) + } + + @Test + fun initialize_rotationSeascape_activeCornerIsTopLeft() { + setRotation(ROTATION_SEASCAPE) + + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView) + } + + @Test + fun initialize_rotationUpsideDown_activeCornerIsBottomLeft() { + setRotation(ROTATION_UPSIDE_DOWN) + + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView) + } + + @Test + fun initialize_rotationPortrait_rtl_activeCornerIsTopLeft() { + setRotation(ROTATION_NONE) + + enableRtl() + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView) + } + + @Test + fun initialize_rotationLandscape_rtl_activeCornerIsTopRight() { + setRotation(ROTATION_LANDSCAPE) + + enableRtl() + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView) + } + + @Test + fun initialize_rotationSeascape_rtl_activeCornerIsBottomLeft() { + setRotation(ROTATION_SEASCAPE) + + enableRtl() + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView) + } + + @Test + fun initialize_rotationUpsideDown_rtl_activeCornerIsBottomRight() { + setRotation(ROTATION_UPSIDE_DOWN) + + enableRtl() + val controller = createAndInitializeController() + + assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT) + assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView) + } + + private fun setRotation(rotation: Int) { + whenever(mockDisplay.rotation).thenReturn(rotation) + } + private fun initDotView(): View = View(context).also { it.layoutParams = FrameLayout.LayoutParams(/* width = */ 0, /* height = */ 0) } + + private fun enableRtl() { + configurationController.notifyLayoutDirectionChanged(isRtl = true) + } } private const val DISPLAY_WIDTH = 1234 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt new file mode 100644 index 000000000000..bec8cfec399b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt @@ -0,0 +1,237 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.domain.interactor + +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.statusbar.notification.collection.render.NotifStats +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ActiveNotificationsInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val activeNotificationListRepository = kosmos.activeNotificationListRepository + + private val underTest = kosmos.activeNotificationsInteractor + + @Test + fun testAllNotificationsCount() = + testScope.runTest { + val count by collectLastValue(underTest.allNotificationsCount) + + activeNotificationListRepository.setActiveNotifs(5) + runCurrent() + + assertThat(count).isEqualTo(5) + assertThat(underTest.allNotificationsCountValue).isEqualTo(5) + } + + @Test + fun areAnyNotificationsPresent_isTrue() = + testScope.runTest { + val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent) + + activeNotificationListRepository.setActiveNotifs(2) + runCurrent() + + assertThat(areAnyNotificationsPresent).isTrue() + assertThat(underTest.areAnyNotificationsPresentValue).isTrue() + } + + @Test + fun areAnyNotificationsPresent_isFalse() = + testScope.runTest { + val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent) + + activeNotificationListRepository.setActiveNotifs(0) + runCurrent() + + assertThat(areAnyNotificationsPresent).isFalse() + assertThat(underTest.areAnyNotificationsPresentValue).isFalse() + } + + @Test + fun testActiveNotificationRanks_sizeMatches() { + testScope.runTest { + val activeNotificationRanks by collectLastValue(underTest.activeNotificationRanks) + + activeNotificationListRepository.setActiveNotifs(5) + runCurrent() + + assertThat(activeNotificationRanks!!.size).isEqualTo(5) + } + } + + @Test + fun clearableNotifications_whenHasClearableAlertingNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = true, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = false, + ) + runCurrent() + + assertThat(hasClearable).isTrue() + } + + @Test + fun hasClearableNotifications_whenHasClearableSilentNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = true, + ) + runCurrent() + + assertThat(hasClearable).isTrue() + } + + @Test + fun testHasClearableNotifications_whenHasNoNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 0, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = false, + ) + runCurrent() + + assertThat(hasClearable).isFalse() + } + + @Test + fun hasClearableAlertingNotifications_whenHasClearableSilentNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableAlertingNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = true, + ) + runCurrent() + + assertThat(hasClearable).isFalse() + } + + @Test + fun hasClearableAlertingNotifications_whenHasNoClearableNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableAlertingNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = true, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = true, + hasClearableSilentNotifs = false, + ) + runCurrent() + + assertThat(hasClearable).isFalse() + } + + @Test + fun hasClearableAlertingNotifications_whenHasAlertingNotifs() = + testScope.runTest { + val hasClearable by collectLastValue(underTest.hasClearableAlertingNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = true, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = false, + ) + runCurrent() + + assertThat(hasClearable).isTrue() + } + + @Test + fun hasNonClearableSilentNotifications_whenHasNonClearableSilentNotifs() = + testScope.runTest { + val hasNonClearable by collectLastValue(underTest.hasNonClearableSilentNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = true, + hasClearableSilentNotifs = false, + ) + runCurrent() + + assertThat(hasNonClearable).isTrue() + } + + @Test + fun testHasNonClearableSilentNotifications_whenHasClearableSilentNotifs() = + testScope.runTest { + val hasNonClearable by collectLastValue(underTest.hasNonClearableSilentNotifications) + + activeNotificationListRepository.notifStats.value = + NotifStats( + numActiveNotifs = 2, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = true, + ) + runCurrent() + + assertThat(hasNonClearable).isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index cc4ebd4aa4c3..c01f1c71fdd3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -27,7 +27,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.LaunchableView import com.android.systemui.assist.AssistManager import com.android.systemui.keyguard.KeyguardViewMediator @@ -78,7 +78,7 @@ class ActivityStarterImplTest : SysuiTestCase() { @Mock private lateinit var shadeController: ShadeController @Mock private lateinit var shadeViewController: ShadeViewController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager - @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator + @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var statusBarWindowController: StatusBarWindowController @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController @@ -109,7 +109,7 @@ class ActivityStarterImplTest : SysuiTestCase() { shadeAnimationInteractor, Lazy { statusBarKeyguardViewManager }, Lazy { notifShadeWindowController }, - activityLaunchAnimator, + mActivityTransitionAnimator, context, DISPLAY_ID, lockScreenUserManager, @@ -149,7 +149,7 @@ class ActivityStarterImplTest : SysuiTestCase() { override fun setShouldBlockVisibilityChanges(block: Boolean) {} } parent.addView(view) - val controller = ActivityLaunchAnimator.Controller.fromView(view) + val controller = ActivityTransitionAnimator.Controller.fromView(view) whenever(pendingIntent.isActivity).thenReturn(true) whenever(keyguardStateController.isShowing).thenReturn(true) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) @@ -163,7 +163,7 @@ class ActivityStarterImplTest : SysuiTestCase() { ) mainExecutor.runAllReady() - verify(activityLaunchAnimator) + verify(mActivityTransitionAnimator) .startPendingIntentWithAnimation( nullable(), eq(true), diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 643420989d1a..1126ec3382a4 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -20,7 +20,7 @@ import android.content.Intent; import android.os.UserHandle; import android.view.View; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.plugins.annotations.ProvidesInterface; /** @@ -54,7 +54,7 @@ public interface ActivityStarter { */ void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentUiThreadCallback, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityTransitionAnimator.Controller animationController); /** * Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching @@ -64,7 +64,7 @@ public interface ActivityStarter { */ void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent, @Nullable Runnable intentSentUiThreadCallback, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityTransitionAnimator.Controller animationController); /** * The intent flag can be specified in startActivity(). @@ -72,26 +72,26 @@ public interface ActivityStarter { void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags); void startActivity(Intent intent, boolean dismissShade); default void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController) { + @Nullable ActivityTransitionAnimator.Controller animationController) { startActivity(intent, dismissShade, animationController, false /* showOverLockscreenWhenLocked */); } void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController, + @Nullable ActivityTransitionAnimator.Controller animationController, boolean showOverLockscreenWhenLocked); void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController, + @Nullable ActivityTransitionAnimator.Controller animationController, boolean showOverLockscreenWhenLocked, UserHandle userHandle); void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade); void startActivity(Intent intent, boolean dismissShade, Callback callback); void postStartActivityDismissingKeyguard(Intent intent, int delay); void postStartActivityDismissingKeyguard(Intent intent, int delay, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityTransitionAnimator.Controller animationController); /** Posts a start activity intent that dismisses keyguard. */ void postStartActivityDismissingKeyguard(Intent intent, int delay, - @Nullable ActivityLaunchAnimator.Controller animationController, + @Nullable ActivityTransitionAnimator.Controller animationController, @Nullable String customMessage); void postStartActivityDismissingKeyguard(PendingIntent intent); @@ -100,7 +100,7 @@ public interface ActivityStarter { * animation controller that should be used for the activity launch animation. */ void postStartActivityDismissingKeyguard(PendingIntent intent, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityTransitionAnimator.Controller animationController); void postQSRunnableDismissingKeyguard(Runnable runnable); @@ -123,7 +123,7 @@ public interface ActivityStarter { boolean disallowEnterPictureInPictureWhileLaunching, Callback callback, int flags, - @Nullable ActivityLaunchAnimator.Controller animationController, + @Nullable ActivityTransitionAnimator.Controller animationController, UserHandle userHandle); /** Execute a runnable after dismissing keyguard. */ diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 15688c5202ac..b30e4a2786a6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1932,7 +1932,7 @@ <!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] --> <string name="group_system_lock_screen">Lock screen</string> <!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] --> - <string name="group_system_quick_memo">Open notes</string> + <string name="group_system_quick_memo">Take a note</string> <!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] --> <string name="keyboard_shortcut_group_system_multitasking">System multitasking</string> diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1a34cc4fc3a9..91157d7ebb20 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; @@ -172,6 +173,7 @@ public class Dependency { @Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy; @Inject Lazy<DialogLaunchAnimator> mDialogLaunchAnimatorLazy; @Inject Lazy<UserTracker> mUserTrackerLazy; + @Inject Lazy<StatusBarWindowController> mStatusBarWindowControllerLazy; @Inject public Dependency() { @@ -226,6 +228,7 @@ public class Dependency { mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get); mProviders.put(DialogLaunchAnimator.class, mDialogLaunchAnimatorLazy::get); mProviders.put(UserTracker.class, mUserTrackerLazy::get); + mProviders.put(StatusBarWindowController.class, mStatusBarWindowControllerLazy::get); Dependency.setInstance(this); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt index aab0b1e99e09..e88aaf015f87 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt @@ -22,6 +22,7 @@ import android.content.BroadcastReceiver import android.content.ContentProvider import android.content.Context import android.content.Intent +import android.util.Log import androidx.core.app.AppComponentFactory import com.android.systemui.dagger.ContextComponentHelper import com.android.systemui.dagger.SysUIComponent @@ -90,8 +91,7 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() { return app } - @UsesReflection( - KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject")) + @UsesReflection(KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject")) override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider { val contentProvider = super.instantiateProviderCompat(cl, className) if (contentProvider is ContextInitializer) { @@ -103,12 +103,11 @@ abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() { .getMethod("inject", contentProvider.javaClass) injectMethod.invoke(rootComponent, contentProvider) } catch (e: NoSuchMethodException) { - throw RuntimeException("No injector for class: " + contentProvider.javaClass, e) + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) } catch (e: IllegalAccessException) { - throw RuntimeException("Injector inaccessible for class: " + - contentProvider.javaClass, e) + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) } catch (e: InvocationTargetException) { - throw RuntimeException("Error while injecting: " + contentProvider.javaClass, e) + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) } initializer } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 66fe4b36567d..716209d18a01 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -66,7 +66,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.dagger.BiometricsBackground; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; @@ -162,7 +162,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mUnlockedScreenOffAnimationController; @NonNull private final LatencyTracker mLatencyTracker; @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener; - @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; + @NonNull private final ActivityTransitionAnimator mActivityTransitionAnimator; @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; @NonNull private final ShadeInteractor mShadeInteractor; @Nullable private final TouchProcessor mTouchProcessor; @@ -287,7 +287,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { event, fromUdfpsView ), - mActivityLaunchAnimator, + mActivityTransitionAnimator, mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsKeyguardAccessibilityDelegate, @@ -663,7 +663,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @NonNull SystemUIDialogManager dialogManager, @NonNull LatencyTracker latencyTracker, - @NonNull ActivityLaunchAnimator activityLaunchAnimator, + @NonNull ActivityTransitionAnimator activityTransitionAnimator, @NonNull @BiometricsBackground Executor biometricsExecutor, @NonNull PrimaryBouncerInteractor primaryBouncerInteractor, @NonNull ShadeInteractor shadeInteractor, @@ -706,7 +706,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mSystemClock = systemClock; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLatencyTracker = latencyTracker; - mActivityLaunchAnimator = activityLaunchAnimator; + mActivityTransitionAnimator = activityTransitionAnimator; mSensorProps = new FingerprintSensorPropertiesInternal( -1 /* sensorId */, SensorProperties.STRENGTH_CONVENIENCE, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 4ea5f4c7af0f..a209eae5673b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -44,7 +44,7 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan import androidx.annotation.LayoutRes import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.biometrics.ui.binder.UdfpsTouchOverlayBinder @@ -100,7 +100,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( @RequestReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, private val onTouch: (View, MotionEvent, Boolean) -> Boolean, - private val activityLaunchAnimator: ActivityLaunchAnimator, + private val activityTransitionAnimator: ActivityTransitionAnimator, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val isDebuggable: Boolean = Build.IS_DEBUGGABLE, @@ -304,7 +304,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( unlockedScreenOffAnimationController, dialogManager, controller, - activityLaunchAnimator, + activityTransitionAnimator, primaryBouncerInteractor, alternateBouncerInteractor, udfpsKeyguardAccessibilityDelegate, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 7020d05350da..018d92e4e932 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -25,7 +25,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor @@ -69,7 +69,7 @@ open class UdfpsKeyguardViewControllerLegacy( private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, systemUIDialogManager: SystemUIDialogManager, private val udfpsController: UdfpsController, - private val activityLaunchAnimator: ActivityLaunchAnimator, + private val activityTransitionAnimator: ActivityTransitionAnimator, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, @@ -137,20 +137,20 @@ open class UdfpsKeyguardViewControllerLegacy( } } - private val activityLaunchAnimatorListener: ActivityLaunchAnimator.Listener = - object : ActivityLaunchAnimator.Listener { - override fun onLaunchAnimationStart() { + private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener = + object : ActivityTransitionAnimator.Listener { + override fun onTransitionAnimationStart() { isLaunchingActivity = true activityLaunchProgress = 0f updateAlpha() } - override fun onLaunchAnimationEnd() { + override fun onTransitionAnimationEnd() { isLaunchingActivity = false updateAlpha() } - override fun onLaunchAnimationProgress(linearProgress: Float) { + override fun onTransitionAnimationProgress(linearProgress: Float) { activityLaunchProgress = linearProgress updateAlpha() } @@ -370,7 +370,7 @@ open class UdfpsKeyguardViewControllerLegacy( updatePauseAuth() keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this - activityLaunchAnimator.addListener(activityLaunchAnimatorListener) + activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener) view.startIconAsyncInflate { val animationViewInternal: View = view.requireViewById(R.id.udfps_animation_view_internal) @@ -389,7 +389,7 @@ open class UdfpsKeyguardViewControllerLegacy( if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) { lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null } - activityLaunchAnimator.removeListener(activityLaunchAnimatorListener) + activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener) keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback) } @@ -536,7 +536,7 @@ open class UdfpsKeyguardViewControllerLegacy( val udfpsActivityLaunchAlphaMultiplier = 1f - (activityLaunchProgress * - (ActivityLaunchAnimator.TIMINGS.totalDuration / 83)) + (ActivityTransitionAnimator.TIMINGS.totalDuration / 83)) .coerceIn(0f, 1f) alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt() } diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt index dc07c1b25678..0bad33b8f920 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -20,6 +20,7 @@ import com.android.systemui.communal.data.db.CommunalDatabaseModule import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule import com.android.systemui.communal.data.repository.CommunalRepositoryModule +import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule import com.android.systemui.communal.widgets.EditWidgetsActivityStarter @@ -36,6 +37,7 @@ import dagger.Module CommunalWidgetRepositoryModule::class, CommunalDatabaseModule::class, CommunalPrefsRepositoryModule::class, + CommunalSettingsRepositoryModule::class, ] ) interface CommunalModule { diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt new file mode 100644 index 000000000000..83a5bdb14ebd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt @@ -0,0 +1,69 @@ +/* + * 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.communal.data.model + +import com.android.systemui.log.table.Diffable +import com.android.systemui.log.table.TableRowLogger +import java.util.EnumSet + +/** Reasons that communal is disabled, primarily for logging. */ +enum class DisabledReason(val loggingString: String) { + /** Communal should be disabled due to invalid current user */ + DISABLED_REASON_INVALID_USER("invalidUser"), + /** Communal should be disabled due to the flag being off */ + DISABLED_REASON_FLAG("flag"), + /** Communal should be disabled because the user has turned off the setting */ + DISABLED_REASON_USER_SETTING("userSetting"), + /** Communal is disabled by the device policy app */ + DISABLED_REASON_DEVICE_POLICY("devicePolicy"), +} + +/** + * Model representing the reasons communal hub should be disabled. Allows logging reasons separately + * for debugging. + */ +@JvmInline +value class CommunalEnabledState( + private val disabledReasons: EnumSet<DisabledReason> = + EnumSet.noneOf(DisabledReason::class.java) +) : Diffable<CommunalEnabledState>, Set<DisabledReason> by disabledReasons { + + /** Creates [CommunalEnabledState] with a single reason for being disabled */ + constructor(reason: DisabledReason) : this(EnumSet.of(reason)) + + /** Checks if there are any reasons communal should be disabled. If none, returns true. */ + val enabled: Boolean + get() = isEmpty() + + override fun logDiffs(prevVal: CommunalEnabledState, row: TableRowLogger) { + for (reason in DisabledReason.entries) { + val newVal = contains(reason) + if (newVal != prevVal.contains(reason)) { + row.logChange( + columnName = reason.loggingString, + value = newVal, + ) + } + } + } + + override fun logFull(row: TableRowLogger) { + for (reason in DisabledReason.entries) { + row.logChange(columnName = reason.loggingString, value = contains(reason)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index addd880f2079..4a06585f5a70 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -16,22 +16,14 @@ package com.android.systemui.communal.data.repository -import com.android.systemui.Flags.communalHub import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.user.data.repository.UserRepository -import com.android.systemui.util.settings.SecureSettings -import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import javax.inject.Inject -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -39,26 +31,13 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.withContext /** Encapsulates the state of communal mode. */ interface CommunalRepository { - /** Whether communal features are enabled. */ - val isCommunalEnabled: Boolean - - /** - * A {@link StateFlow} that tracks whether communal hub is enabled (it can be disabled in - * settings). - */ - val communalEnabledState: StateFlow<Boolean> - /** Whether the communal hub is showing. */ val isCommunalHubShowing: Flow<Boolean> @@ -87,37 +66,11 @@ interface CommunalRepository { class CommunalRepositoryImpl @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Background backgroundScope: CoroutineScope, - @Background private val backgroundDispatcher: CoroutineDispatcher, - private val featureFlagsClassic: FeatureFlagsClassic, sceneContainerFlags: SceneContainerFlags, sceneContainerRepository: SceneContainerRepository, - userRepository: UserRepository, - private val secureSettings: SecureSettings ) : CommunalRepository { - private val communalEnabledSettingState: Flow<Boolean> = - userRepository.selectedUserInfo - .flatMapLatest { userInfo -> observeSettings(userInfo.id) } - .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed()) - - override val communalEnabledState: StateFlow<Boolean> = - if (featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()) { - communalEnabledSettingState - .filterNotNull() - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = true - ) - } else { - MutableStateFlow(false) - } - - override val isCommunalEnabled: Boolean - get() = communalEnabledState.value - private val _desiredScene: MutableStateFlow<CommunalSceneKey> = MutableStateFlow(CommunalSceneKey.DEFAULT) override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow() @@ -153,26 +106,4 @@ constructor( } else { desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal } } - - private fun observeSettings(userId: Int): Flow<Boolean> = - secureSettings - .observerFlow( - userId = userId, - names = - arrayOf( - GLANCEABLE_HUB_ENABLED, - ) - ) - // Force an update - .onStart { emit(Unit) } - .map { readFromSettings(userId) } - - private suspend fun readFromSettings(userId: Int): Boolean = - withContext(backgroundDispatcher) { - secureSettings.getIntForUser(GLANCEABLE_HUB_ENABLED, 1, userId) == 1 - } - - companion object { - private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled" - } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt new file mode 100644 index 000000000000..201b04927ea3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt @@ -0,0 +1,126 @@ +/* + * 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.communal.data.repository + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL +import android.content.IntentFilter +import android.content.pm.UserInfo +import com.android.systemui.Flags.communalHub +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.communal.data.model.CommunalEnabledState +import com.android.systemui.communal.data.model.DisabledReason +import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY +import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG +import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER +import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.util.kotlin.emitOnStart +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import java.util.EnumSet +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +interface CommunalSettingsRepository { + /** A [CommunalEnabledState] for the specified user. */ + fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> +} + +@SysUISingleton +class CommunalSettingsRepositoryImpl +@Inject +constructor( + @Background private val bgDispatcher: CoroutineDispatcher, + private val featureFlagsClassic: FeatureFlagsClassic, + private val secureSettings: SecureSettings, + private val broadcastDispatcher: BroadcastDispatcher, + private val devicePolicyManager: DevicePolicyManager, +) : CommunalSettingsRepository { + + private val flagEnabled: Boolean by lazy { + featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() + } + + override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> { + if (!user.isMain) { + return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER)) + } + if (!flagEnabled) { + return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG)) + } + return combine( + getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING), + getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY), + ) { reasons -> + reasons.filterNotNull() + } + .map { reasons -> + if (reasons.isEmpty()) { + EnumSet.noneOf(DisabledReason::class.java) + } else { + EnumSet.copyOf(reasons) + } + } + .map { reasons -> CommunalEnabledState(reasons) } + .flowOn(bgDispatcher) + } + + private fun getEnabledByUser(user: UserInfo): Flow<Boolean> = + secureSettings + .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED)) + // Force an update + .onStart { emit(Unit) } + .map { + secureSettings.getIntForUser( + GLANCEABLE_HUB_ENABLED, + ENABLED_SETTING_DEFAULT, + user.id, + ) == 1 + } + + private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> = + broadcastDispatcher + .broadcastFlow( + filter = + IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), + user = user.userHandle + ) + .emitOnStart() + .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) } + + companion object { + const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled" + private const val ENABLED_SETTING_DEFAULT = 1 + } +} + +private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean = + (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0 + +private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled -> + if (enabled) null else reason +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt new file mode 100644 index 000000000000..a931d3f7ec5b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.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.communal.data.repository + +import dagger.Binds +import dagger.Module + +@Module +interface CommunalSettingsRepositoryModule { + @Binds + fun communalSettingsRepository(impl: CommunalSettingsRepositoryImpl): CommunalSettingsRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt index 2ac9d053f8d0..e4c91957ee56 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt @@ -54,11 +54,12 @@ interface CommunalWidgetRepository { configurator: WidgetConfigurator? = null ) {} - /** Delete a widget by id from the database. */ - fun deleteWidgetFromDb(widgetId: Int) {} - - /** Delete a widget by id from app widget host. */ - fun deleteWidgetFromHost(widgetId: Int) {} + /** + * Delete a widget by id from the database and app widget host. + * + * @param widgetId id of the widget to remove. + */ + fun deleteWidget(widgetId: Int) {} /** * Update the order of widgets in the database. @@ -146,23 +147,15 @@ constructor( } } - override fun deleteWidgetFromDb(widgetId: Int) { + override fun deleteWidget(widgetId: Int) { bgScope.launch { if (communalWidgetDao.deleteWidgetById(widgetId)) { - logger.i("Deleted widget with id $widgetId from DB .") - } else { - logger.w("Widget with id $widgetId cannot be deleted from DB.") + appWidgetHost.deleteAppWidgetId(widgetId) + logger.i("Deleted widget with id $widgetId.") } } } - override fun deleteWidgetFromHost(widgetId: Int) { - bgScope.launch { - appWidgetHost.deleteAppWidgetId(widgetId) - logger.i("Deleted widget with id $widgetId.") - } - } - override fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) { bgScope.launch { communalWidgetDao.updateWidgetOrder(widgetIdToPriorityMap) diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 950ac3c3aae6..b4f4099af124 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -42,7 +42,6 @@ import com.android.systemui.log.dagger.CommunalTableLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.smartspace.data.repository.SmartspaceRepository -import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.not import com.android.systemui.util.kotlin.BooleanFlowOperators.or @@ -74,8 +73,8 @@ constructor( private val communalPrefsRepository: CommunalPrefsRepository, mediaRepository: CommunalMediaRepository, smartspaceRepository: SmartspaceRepository, - userRepository: UserRepository, keyguardInteractor: KeyguardInteractor, + private val communalSettingsInteractor: CommunalSettingsInteractor, private val appWidgetHost: CommunalAppWidgetHost, private val editWidgetsActivityStarter: EditWidgetsActivityStarter, @CommunalLog logBuffer: LogBuffer, @@ -90,13 +89,12 @@ constructor( /** Whether communal features are enabled. */ val isCommunalEnabled: Boolean - get() = communalRepository.isCommunalEnabled + get() = communalSettingsInteractor.isCommunalEnabled.value /** Whether communal features are enabled and available. */ val isCommunalAvailable: Flow<Boolean> = and( - communalRepository.communalEnabledState, - userRepository.selectedUserInfo.map { it.isMain }, + communalSettingsInteractor.isCommunalEnabled, not(keyguardInteractor.isEncryptedOrLockdown), or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming) ) @@ -238,14 +236,11 @@ constructor( ) = widgetRepository.addWidget(componentName, priority, configurator) /** - * Delete a widget by id from the database. [CommunalAppWidgetHostStartable] invokes this - * function to manage the deletion from the database for uninstalled or user-deleted widgets, - * following the removal of a widget from the host. + * Delete a widget by id. Called when user deletes a widget from the hub or a widget is + * uninstalled from App widget host. */ - fun deleteWidgetFromDb(id: Int) = widgetRepository.deleteWidgetFromDb(id) + fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id) - /** Delete a widget by id from AppWidgetHost. Called when user deletes a widget from the hub */ - fun deleteWidgetFromHost(id: Int) = widgetRepository.deleteWidgetFromHost(id) /** * Reorder the widgets. * diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt new file mode 100644 index 000000000000..0b096ce67fc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt @@ -0,0 +1,58 @@ +/* + * 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.communal.domain.interactor + +import com.android.systemui.communal.data.model.CommunalEnabledState +import com.android.systemui.communal.data.repository.CommunalSettingsRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.dagger.CommunalTableLog +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.logDiffsForTable +import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class CommunalSettingsInteractor +@Inject +constructor( + @Background private val bgScope: CoroutineScope, + private val repository: CommunalSettingsRepository, + userInteractor: SelectedUserInteractor, + @CommunalTableLog tableLogBuffer: TableLogBuffer, +) { + /** Whether or not communal is enabled for the currently selected user. */ + val isCommunalEnabled: StateFlow<Boolean> = + userInteractor.selectedUserInfo + .flatMapLatest { user -> repository.getEnabledState(user) } + .logDiffsForTable( + tableLogBuffer = tableLogBuffer, + columnPrefix = "disabledReason", + initialValue = CommunalEnabledState() + ) + .map { model -> model.enabled } + // Start this eagerly since the value is accessed synchronously in many places. + .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false) +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt index 1404ee20cb72..25dfc02a5d98 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt @@ -28,17 +28,18 @@ import com.android.systemui.log.table.logDiffsForTable import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformWhile import kotlinx.coroutines.launch /** Encapsulates business-logic related to communal tutorial state. */ @@ -51,6 +52,7 @@ constructor( private val communalTutorialRepository: CommunalTutorialRepository, keyguardInteractor: KeyguardInteractor, private val communalRepository: CommunalRepository, + private val communalSettingsInteractor: CommunalSettingsInteractor, communalInteractor: CommunalInteractor, @CommunalTableLog tableLogBuffer: TableLogBuffer, ) { @@ -110,20 +112,24 @@ constructor( return null } - private var job: Job? = null private fun listenForTransitionToUpdateTutorialState() { - if (!communalRepository.isCommunalEnabled) { - return - } - job = - scope.launch { - tutorialStateToUpdate.collect { - communalTutorialRepository.setTutorialState(it) - if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) { - job?.cancel() + scope.launch { + communalSettingsInteractor.isCommunalEnabled + .flatMapLatest { enabled -> + if (!enabled) { + emptyFlow() + } else { + tutorialStateToUpdate } } - } + .transformWhile { tutorialState -> + emit(tutorialState) + tutorialState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED + } + .collect { tutorialState -> + communalTutorialRepository.setTutorialState(tutorialState) + } + } } init { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 69d55815fe01..0b355cc10db5 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -62,7 +62,7 @@ constructor( override val reorderingWidgets: StateFlow<Boolean> get() = _reorderingWidgets - override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidgetFromHost(id) + override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id) override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) = communalInteractor.updateWidgetOrder(widgetIdToPriorityMap) diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt index 6fd0fbe80fd7..4ddd7681dd98 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt @@ -50,7 +50,7 @@ constructor( .launchIn(bgScope) appWidgetHost.appWidgetIdToRemove - .onEach { appWidgetId -> communalInteractor.deleteWidgetFromDb(appWidgetId) } + .onEach { appWidgetId -> communalInteractor.deleteWidget(id = appWidgetId) } .launchIn(bgScope) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index afa7fa9e8f77..4c1e77bc47f8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.widgets import android.app.PendingIntent import android.view.View import android.widget.RemoteViews -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.view.getNearestParent import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject @@ -42,7 +42,7 @@ constructor( private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean { val hostView = view.getNearestParent<CommunalAppWidgetHostView>() - val animationController = hostView?.let(ActivityLaunchAnimator.Controller::fromView) + val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView) activityStarter.startPendingIntentMaybeDismissingKeyguard( pendingIntent, diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java index 9d4ed20ca582..afa23755d937 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java +++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java @@ -35,7 +35,7 @@ import androidx.annotation.Nullable; import com.android.internal.logging.UiEventLogger; import com.android.settingslib.Utils; import com.android.systemui.CoreStartable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent; import com.android.systemui.controls.ControlsServiceInfo; import com.android.systemui.controls.dagger.ControlsComponent; @@ -275,8 +275,9 @@ public class DreamHomeControlsComplication implements Complication { .putExtra(ControlsUiController.EXTRA_ANIMATE, true) .putExtra(ControlsUiController.EXIT_TO_DREAM, true); - final ActivityLaunchAnimator.Controller controller = - v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */) + final ActivityTransitionAnimator.Controller controller = + v != null + ? ActivityTransitionAnimator.Controller.fromView(v, null /* cujType */) : null; if (mControlsComponent.getVisibility() == AVAILABLE) { // Controls can be made visible. diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java index 05279fcdf51c..e5c705f608b9 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java @@ -17,15 +17,21 @@ package com.android.systemui.dreams.touch; import static com.android.systemui.dreams.touch.dagger.ShadeModule.COMMUNAL_GESTURE_INITIATION_WIDTH; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.graphics.Rect; import android.graphics.Region; import android.view.GestureDetector; import android.view.MotionEvent; +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; + +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.statusbar.phone.CentralSurfaces; import java.util.Optional; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; @@ -34,17 +40,49 @@ import javax.inject.Named; public class CommunalTouchHandler implements DreamTouchHandler { private final int mInitiationWidth; private final Optional<CentralSurfaces> mCentralSurfaces; + private final Lifecycle mLifecycle; + private final CommunalInteractor mCommunalInteractor; + private Boolean mIsEnabled = false; + + @VisibleForTesting + final Consumer<Boolean> mIsCommunalAvailableCallback = + isAvailable -> { + setIsEnabled(isAvailable); + }; @Inject public CommunalTouchHandler( Optional<CentralSurfaces> centralSurfaces, - @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth) { + @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth, + CommunalInteractor communalInteractor, + Lifecycle lifecycle) { mInitiationWidth = initiationWidth; mCentralSurfaces = centralSurfaces; + mLifecycle = lifecycle; + mCommunalInteractor = communalInteractor; + + collectFlow( + mLifecycle, + mCommunalInteractor.isCommunalAvailable(), + mIsCommunalAvailableCallback + ); + } + + @Override + public Boolean isEnabled() { + return mIsEnabled; + } + + @Override + public void setIsEnabled(Boolean enabled) { + mIsEnabled = enabled; } @Override public void onSessionStart(TouchSession session) { + if (!mIsEnabled) { + return; + } mCentralSurfaces.ifPresent(surfaces -> handleSessionStart(surfaces, session)); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java index aca621bd9e55..55a9c0c4de99 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java @@ -292,6 +292,9 @@ public class DreamOverlayTouchMonitor { new HashMap<>(); for (DreamTouchHandler handler : mHandlers) { + if (!handler.isEnabled()) { + continue; + } final Rect maxBounds = mDisplayHelper.getMaxBounds(ev.getDisplayId(), TYPE_APPLICATION_OVERLAY); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java index b37010cfc07b..72ad45d1055c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java @@ -86,6 +86,20 @@ public interface DreamTouchHandler { } /** + * Returns whether the handler is enabled to handle touch on dream. + * @return isEnabled state. By default it's true. + */ + default Boolean isEnabled() { + return true; + } + + /** + * Sets whether to enable the handler to handle touch on dream. + * @param enabled new value to be set whether to enable the handler. + */ + default void setIsEnabled(Boolean enabled){} + + /** * Returns the region the touch handler is interested in. By default, no region is specified, * indicating the entire screen should be considered. * @param region A {@link Region} that is passed in to the target entry touch region. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 39bbf077656e..0ee924dc6829 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -130,7 +130,7 @@ import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.TransitionAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; @@ -957,8 +957,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * Animation launch controller for activities that occlude the keyguard. */ @VisibleForTesting - final ActivityLaunchAnimator.Controller mOccludeAnimationController = - new ActivityLaunchAnimator.Controller() { + final ActivityTransitionAnimator.Controller mOccludeAnimationController = + new ActivityTransitionAnimator.Controller() { @Override public void onTransitionAnimationStart(boolean isExpandingFullyAbove) { mOccludeAnimationPlaying = true; @@ -966,7 +966,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } @Override - public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) { + public void onTransitionAnimationCancelled( + @Nullable Boolean newKeyguardOccludedState) { Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: " + mOccluded); mOccludeAnimationPlaying = false; @@ -1003,7 +1004,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void setTransitionContainer(@NonNull ViewGroup transitionContainer) { // No-op, launch container is always the shade. Log.wtf(TAG, "Someone tried to change the launch container for the " - + "ActivityLaunchAnimator, which should never happen."); + + "ActivityTransitionAnimator, which should never happen."); } @NonNull @@ -1167,8 +1168,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, /** * Animation controller for activities that unocclude the keyguard. This does not use the - * ActivityLaunchAnimator since we're just translating down, rather than emerging from a view - * or the power button. + * ActivityTransitionAnimator since we're just translating down, rather than emerging from a + * view or the power button. */ private final IRemoteAnimationRunner mUnoccludeAnimationRunner = new IRemoteAnimationRunner.Stub() { @@ -1347,7 +1348,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private boolean mWallpaperSupportsAmbientMode; private final KeyguardTransitions mKeyguardTransitions; - private final Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator; + private final Lazy<ActivityTransitionAnimator> mActivityTransitionAnimator; private final Lazy<ScrimController> mScrimControllerLazy; private final IActivityTaskManager mActivityTaskManagerService; @@ -1394,7 +1395,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, WallpaperRepository wallpaperRepository, Lazy<ShadeController> shadeControllerLazy, Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, - Lazy<ActivityLaunchAnimator> activityLaunchAnimator, + Lazy<ActivityTransitionAnimator> activityTransitionAnimator, Lazy<ScrimController> scrimControllerLazy, IActivityTaskManager activityTaskManagerService, FeatureFlags featureFlags, @@ -1459,7 +1460,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mJavaAdapter = javaAdapter; mWallpaperRepository = wallpaperRepository; - mActivityLaunchAnimator = activityLaunchAnimator; + mActivityTransitionAnimator = activityTransitionAnimator; mScrimControllerLazy = scrimControllerLazy; mActivityTaskManagerService = activityTaskManagerService; @@ -3812,15 +3813,15 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, /** * Implementation of RemoteAnimationRunner that creates a new - * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the + * {@link ActivityTransitionAnimator.Runner} whenever onAnimationStart is called, delegating the * remote animation methods to that runner. */ private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub { - private final ActivityLaunchAnimator.Controller mActivityLaunchController; - @Nullable private ActivityLaunchAnimator.Runner mRunner; + private final ActivityTransitionAnimator.Controller mActivityLaunchController; + @Nullable private ActivityTransitionAnimator.Runner mRunner; - ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) { + ActivityLaunchRemoteAnimationRunner(ActivityTransitionAnimator.Controller controller) { mActivityLaunchController = controller; } @@ -3837,7 +3838,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController); + mRunner = mActivityTransitionAnimator.get().createRunner(mActivityLaunchController); mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); } } @@ -3850,7 +3851,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, extends ActivityLaunchRemoteAnimationRunner { OccludeActivityLaunchRemoteAnimationRunner( - ActivityLaunchAnimator.Controller controller) { + ActivityTransitionAnimator.Controller controller) { super(controller); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 70da3e7ad1fa..0b227fa7f5a6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -35,7 +35,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.CoreStartable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; @@ -150,7 +150,7 @@ public interface KeyguardModule { WallpaperRepository wallpaperRepository, Lazy<ShadeController> shadeController, Lazy<NotificationShadeWindowController> notificationShadeWindowController, - Lazy<ActivityLaunchAnimator> activityLaunchAnimator, + Lazy<ActivityTransitionAnimator> activityTransitionAnimator, Lazy<ScrimController> scrimControllerLazy, IActivityTaskManager activityTaskManagerService, FeatureFlags featureFlags, @@ -196,7 +196,7 @@ public interface KeyguardModule { wallpaperRepository, shadeController, notificationShadeWindowController, - activityLaunchAnimator, + activityTransitionAnimator, scrimControllerLazy, activityTaskManagerService, featureFlags, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 96e83b0ca0f8..3630b4038357 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -32,7 +32,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.settingslib.Utils -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.animation.view.LaunchableLinearLayout import com.android.systemui.common.shared.model.Icon @@ -534,7 +534,7 @@ object KeyguardBottomAreaViewBinder { activityStarter.postStartActivityDismissingKeyguard( WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD), /* delay= */ 0, - /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view), + /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view), /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt index f67cb684b7a6..b1adef46e782 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.common.ui.binder.TextViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel @@ -115,7 +115,7 @@ object KeyguardSettingsViewBinder { activityStarter.postStartActivityDismissingKeyguard( WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD), /* delay= */ 0, - /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view), + /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view), /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index 6763e0a1b798..f981fd50dc04 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -24,6 +24,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.res.R @@ -100,12 +101,23 @@ constructor( initialValue = false ) - // Needs to use a non application context to get display cutout. - fun getSmallClockTopMargin(context: Context) = + /** Calculates the top margin for the small clock. */ + fun getSmallClockTopMargin(context: Context): Int { + var topMargin: Int + val statusBarHeight = Utils.getStatusBarHeaderHeightKeyguard(context) + if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) { - context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin) + topMargin = + context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin) + if (ComposeLockscreen.isEnabled) { + topMargin -= statusBarHeight + } } else { - context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + - Utils.getStatusBarHeaderHeightKeyguard(context) + topMargin = context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + if (!ComposeLockscreen.isEnabled) { + topMargin += statusBarHeight + } } + return topMargin + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 2b28a71b4a3d..fa185570e7fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor @@ -36,7 +36,7 @@ class LockscreenSceneViewModel constructor( @Application applicationScope: CoroutineScope, deviceEntryInteractor: DeviceEntryInteractor, - communalInteractor: CommunalInteractor, + communalSettingsInteractor: CommunalSettingsInteractor, val longPress: KeyguardLongPressViewModel, val notifications: NotificationsPlaceholderViewModel, ) { @@ -55,10 +55,12 @@ constructor( } /** The key of the scene we should switch to when swiping left. */ - val leftDestinationSceneKey: SceneKey? = - if (communalInteractor.isCommunalEnabled) { - SceneKey.Communal - } else { - null - } + val leftDestinationSceneKey: StateFlow<SceneKey?> = + communalSettingsInteractor.isCommunalEnabled + .map { available -> if (available) SceneKey.Communal else null } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = null, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/media/OWNERS b/packages/SystemUI/src/com/android/systemui/media/OWNERS index b2d00df252ff..69ea57bfd397 100644 --- a/packages/SystemUI/src/com/android/systemui/media/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/media/OWNERS @@ -1,5 +1 @@ per-file MediaProjectionPermissionActivity.java = michaelwr@google.com - -# Haptics team also works on Ringtone -per-file NotificationPlayer.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file RingtonePlayer.java = file:/services/core/java/com/android/server/vibrator/OWNERS 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 5720cc74002b..aa92814a584d 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 @@ -81,8 +81,8 @@ 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.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.GhostedViewLaunchAnimatorController; +import com.android.systemui.animation.ActivityTransitionAnimator; +import com.android.systemui.animation.GhostedViewTransitionAnimatorController; import com.android.systemui.bluetooth.BroadcastDialogController; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.qualifiers.Background; @@ -118,6 +118,8 @@ import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig; import com.android.systemui.surfaceeffects.ripple.RippleShader; import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig; import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController; +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type; +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView; import com.android.systemui.util.ColorUtilKt; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -132,6 +134,7 @@ import kotlin.Unit; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; +import java.util.Random; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -251,6 +254,8 @@ public class MediaControlPanel { private boolean mWasPlaying = false; private boolean mButtonClicked = false; + private final Random mRandom = new Random(); + /** * Initialize a new control panel * @@ -448,7 +453,10 @@ public class MediaControlPanel { MultiRippleView multiRippleView = vh.getMultiRippleView(); mMultiRippleController = new MultiRippleController(multiRippleView); - mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView()); + + TurbulenceNoiseView turbulenceNoiseView = vh.getTurbulenceNoiseView(); + turbulenceNoiseView.setBlendMode(BlendMode.SCREEN); + mTurbulenceNoiseController = new TurbulenceNoiseController(turbulenceNoiseView); mColorSchemeTransition = new ColorSchemeTransition( mContext, mMediaViewHolder, mMultiRippleController, mTurbulenceNoiseController); @@ -587,6 +595,7 @@ public class MediaControlPanel { } // Color will be correctly updated in ColorSchemeTransition. mTurbulenceNoiseController.play( + Type.SIMPLEX_NOISE, mTurbulenceNoiseAnimationConfig ); mMainExecutor.executeDelayed( @@ -1227,22 +1236,23 @@ public class MediaControlPanel { return new TurbulenceNoiseAnimationConfig( /* gridCount= */ 2.14f, TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, + /* noiseOffsetX= */ mRandom.nextFloat(), + /* noiseOffsetY= */ mRandom.nextFloat(), + /* noiseOffsetZ= */ mRandom.nextFloat(), /* noiseMoveSpeedX= */ 0.42f, /* noiseMoveSpeedY= */ 0f, TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), /* backgroundColor= */ Color.BLACK, - /* opacity= */ 51, /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(), /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(), TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, /* easeInDuration= */ 1350f, /* easeOutDuration= */ 1350f, getContext().getResources().getDisplayMetrics().density, - BlendMode.SCREEN, - /* onAnimationEnd= */ null, /* lumaMatteBlendFactor= */ 0.26f, - /* lumaMatteOverallBrightness= */ 0.09f + /* lumaMatteOverallBrightness= */ 0.09f, + /* shouldInverseNoiseLuminosity= */ false ); } private void clearButton(final ImageButton button) { @@ -1309,7 +1319,7 @@ public class MediaControlPanel { } @Nullable - private ActivityLaunchAnimator.Controller buildLaunchAnimatorController( + private ActivityTransitionAnimator.Controller buildLaunchAnimatorController( TransitionLayout player) { if (!(player.getParent() instanceof ViewGroup)) { // TODO(b/192194319): Throw instead of just logging. @@ -1320,7 +1330,7 @@ public class MediaControlPanel { // TODO(b/174236650): Make sure that the carousel indicator also fades out. // TODO(b/174236650): Instrument the animation to measure jank. - return new GhostedViewLaunchAnimatorController(player, + return new GhostedViewTransitionAnimatorController(player, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) { @Override protected float getCurrentTopCornerRadius() { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 523414cfddbf..35e0271c1b8f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -1155,7 +1155,7 @@ constructor( onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS // TODO(b/311234666): revisit logic once interactions between the hub and // shade/keyguard state are finalized - isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB + isCommunalShowing -> LOCATION_COMMUNAL_HUB onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 375a0ce55ac0..687f26871119 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -78,7 +78,7 @@ import com.android.settingslib.media.InfoMediaManager; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.utils.ThreadUtils; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.flags.FeatureFlags; @@ -400,7 +400,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, void tryToLaunchInAppRoutingIntent(String routeId, View view) { ComponentName componentName = mLocalMediaManager.getLinkedItemComponentName(); if (componentName != null) { - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); Intent launchIntent = new Intent(ACTION_TRANSFER_MEDIA); launchIntent.setComponent(componentName); @@ -412,7 +412,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, } void tryToLaunchMediaApplication(View view) { - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); Intent launchIntent = getAppLaunchIntent(); if (launchIntent != null) { @@ -881,7 +881,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, } void launchBluetoothPairing(View view) { - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); if (controller == null || (mKeyGuardManager != null diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt index 0941a2082cfd..3fd9803882ed 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt @@ -11,7 +11,7 @@ import android.view.View import androidx.annotation.WorkerThread import com.android.internal.R import com.android.internal.logging.UiEventLogger -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.appops.AppOpsController import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.flags.FeatureFlags @@ -175,7 +175,7 @@ class HeaderPrivacyIconsController @Inject constructor( startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK uiExecutor.execute { activityStarter.startActivity(startSafetyCenter, true, - ActivityLaunchAnimator.Controller.fromView(privacyChip)) + ActivityTransitionAnimator.Controller.fromView(privacyChip)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index bd13d0686462..b53c2450f4c7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -51,7 +51,7 @@ import androidx.annotation.WorkerThread; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -65,14 +65,14 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.DisplayTracker; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - import dagger.Lazy; import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + public class CustomTile extends QSTileImpl<State> implements TileChangeListener, CustomTileInterface { public static final String PREFIX = "custom("; @@ -540,9 +540,9 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener, } else { Log.i(TAG, "The activity is starting"); - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mViewClicked == null ? null : - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( mViewClicked, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE ); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 529d68407ce9..35cac4b2adb2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -57,7 +57,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.systemui.Dumpable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; @@ -70,7 +70,6 @@ import com.android.systemui.qs.SideLabelTileLayout; import com.android.systemui.qs.logging.QSLogger; import java.io.PrintWriter; -import java.util.ArrayList; /** * Base quick-settings tile, extend this to create a new tile. @@ -412,8 +411,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy * @param view The view from which the opening window will be animated. */ protected void handleLongClick(@Nullable View view) { - ActivityLaunchAnimator.Controller animationController = - view != null ? ActivityLaunchAnimator.Controller.fromView(view, + ActivityTransitionAnimator.Controller animationController = + view != null ? ActivityTransitionAnimator.Controller.fromView(view, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null; mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0, animationController); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt index d98141fcd6b9..688f3ca38087 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt @@ -14,7 +14,7 @@ import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.MetricsLogger import com.android.systemui.res.R -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -75,7 +75,7 @@ class AlarmTile @Inject constructor( override fun handleClick(view: View?) { val animationController = view?.let { - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) } val pendingIntent = lastAlarmInfo?.showIntent diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index a69820868838..690b71100846 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -38,7 +38,7 @@ import com.android.internal.app.MediaRouteDialogPresenter; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; @@ -222,7 +222,7 @@ public class CastTile extends QSTileImpl<BooleanState> { mContext, ROUTE_TYPE_REMOTE_DISPLAY, v -> { - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(v); if (controller == null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index 91b2d971c004..bb175e2a8534 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -26,7 +26,7 @@ import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.MetricsLogger import com.android.systemui.res.R -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE @@ -112,7 +112,7 @@ class DeviceControlsTile @Inject constructor( putExtra(ControlsUiController.EXTRA_ANIMATE, true) } val animationController = view?.let { - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java index f70e27d72d69..de9a08e1cda8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java @@ -27,8 +27,7 @@ import androidx.annotation.Nullable; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.res.R; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -40,6 +39,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import javax.inject.Inject; @@ -108,8 +108,8 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> { return; } - ActivityLaunchAnimator.Controller animationController = - view == null ? null : ActivityLaunchAnimator.Controller.fromView(view, + ActivityTransitionAnimator.Controller animationController = + view == null ? null : ActivityTransitionAnimator.Controller.fromView(view, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE); mActivityStarter.startActivity(intent, true /* dismissShade */, animationController, true /* showOverLockscreenWhenLocked */); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 3b8fb263c27e..1b7322592b41 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -43,7 +43,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -132,8 +132,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override protected void handleClick(@Nullable View view) { - ActivityLaunchAnimator.Controller animationController = - view == null ? null : ActivityLaunchAnimator.Controller.fromView(view, + ActivityTransitionAnimator.Controller animationController = + view == null ? null : ActivityTransitionAnimator.Controller.fromView(view, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE); mUiHandler.post( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt index fe10eaaec793..7192f58218a4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt @@ -22,7 +22,7 @@ import android.content.pm.PackageManager import android.os.UserHandle import android.view.View import com.android.internal.jank.InteractionJankMonitor -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject @@ -53,9 +53,9 @@ constructor( ) : QSTileIntentUserInputHandler { override fun handle(view: View?, intent: Intent) { - val animationController: ActivityLaunchAnimator.Controller? = + val animationController: ActivityTransitionAnimator.Controller? = view?.let { - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, ) @@ -70,9 +70,9 @@ constructor( requestLaunchingDefaultActivity: Boolean ) { if (pendingIntent.isActivity) { - val animationController: ActivityLaunchAnimator.Controller? = + val animationController: ActivityTransitionAnimator.Controller? = view?.let { - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 211b459471de..41de65c18aaa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -75,7 +75,7 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.settingslib.wifi.WifiUtils; import com.android.settingslib.wifi.dpp.WifiDppIntentHelper; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; @@ -748,7 +748,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi } private void startActivity(Intent intent, View view) { - ActivityLaunchAnimator.Controller controller = + ActivityTransitionAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); if (controller == null && mCallback != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 1c7cc007cbc7..df845f559f2e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -35,6 +35,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.util.kotlin.collectFlow import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf /** * Controller that's responsible for the glanceable hub container view and its touch handling. @@ -105,13 +106,9 @@ constructor( */ private var shadeShowing = false - /** Returns true if the glanceable hub is enabled and the container view can be created. */ - fun isEnabled(): Boolean { - return communalInteractor.isCommunalEnabled && isComposeAvailable() - } - - /** Returns a {@link StateFlow} that tracks whether communal hub is available. */ - fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable + /** Returns a flow that tracks whether communal hub is available. */ + fun communalAvailable(): Flow<Boolean> = + if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false) /** * Creates the container view containing the glanceable hub UI. @@ -127,9 +124,6 @@ constructor( /** Override for testing. */ @VisibleForTesting internal fun initView(containerView: View): View { - if (!isEnabled()) { - throw RuntimeException("Glanceable hub is not enabled") - } if (communalContainerView != null) { throw RuntimeException("Communal view has already been initialized") } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 73d229eae972..296849044e33 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -112,7 +112,7 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.Gefingerpoken; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.TransitionAnimator; import com.android.systemui.biometrics.AuthController; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -136,6 +136,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; 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; @@ -259,7 +260,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump /** The parallax amount of the quick settings translation when dragging down the panel. */ public static final float QS_PARALLAX_AMOUNT = 0.175f; private static final long ANIMATION_DELAY_ICON_FADE_IN = - ActivityLaunchAnimator.TIMINGS.getTotalDuration() + ActivityTransitionAnimator.TIMINGS.getTotalDuration() - CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY - 48; private static final int NO_FIXED_DURATION = -1; @@ -2476,6 +2477,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump if (!isKeyguardShowing()) { return 0; } + + if (ComposeLockscreen.isEnabled()) { + return (int) mKeyguardInteractor.getNotificationContainerBounds() + .getValue().getTop(); + } + if (!mKeyguardBypassController.getBypassEnabled()) { if (migrateClocksToBlueprint() && !mSplitShadeEnabled) { return (int) mKeyguardInteractor.getNotificationContainerBounds() @@ -3231,7 +3238,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void applyLaunchAnimationProgress(float linearProgress) { - boolean hideIcons = TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, + boolean hideIcons = TransitionAnimator.getProgress(ActivityTransitionAnimator.TIMINGS, linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; if (hideIcons != mHideIconsDuringLaunchAnimation) { mHideIconsDuringLaunchAnimation = hideIcons; diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index ef820f3dd9ae..aa2d606c5126 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -34,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.bouncer.ui.binder.BouncerViewBinder; @@ -679,7 +679,7 @@ public class NotificationShadeWindowViewController implements Dumpable { void setExpandAnimationRunning(boolean running) { if (mExpandAnimationRunning != running) { // TODO(b/288507023): Remove this log. - if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { + if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { Log.d(TAG, "Setting mExpandAnimationRunning=" + running); } if (running) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index b64e0b7d3187..91340be8182b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -26,7 +26,7 @@ import android.util.Log; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.CoreStartable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.AnimationFeatureFlags; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -190,8 +190,8 @@ public interface CentralSurfacesDependenciesModule { /** */ @Provides @SysUISingleton - static ActivityLaunchAnimator provideActivityLaunchAnimator() { - return new ActivityLaunchAnimator(); + static ActivityTransitionAnimator provideActivityTransitionAnimator() { + return new ActivityTransitionAnimator(); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 618dec22b32c..658625996575 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -424,7 +424,8 @@ open class PrivacyDotViewController @Inject constructor( br = bottomRight val rtl = configurationController.isLayoutRtl - val dc = selectDesignatedCorner(0, rtl) + val currentRotation = RotationUtils.getExactRotation(tl.context) + val dc = selectDesignatedCorner(currentRotation, rtl) val index = dc.cornerIndex() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt index 6f4a7cdaff5e..4af8cb9ba303 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt @@ -2,7 +2,7 @@ package com.android.systemui.statusbar.notification import android.util.MathUtils import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.app.animation.Interpolators import com.android.systemui.animation.TransitionAnimator import kotlin.math.min @@ -58,7 +58,11 @@ class LaunchAnimationParameters( } fun getProgress(delay: Long, duration: Long): Float { - return TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, linearProgress, delay, - duration) + return TransitionAnimator.getProgress( + ActivityTransitionAnimator.TIMINGS, + linearProgress, + delay, + duration + ) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt index 8fc961936abd..eb0870a5de44 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification import android.util.Log import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.TransitionAnimator import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -31,7 +31,7 @@ import kotlin.math.max private const val TAG = "NotificationLaunchAnimatorController" -/** A provider of [NotificationLaunchAnimatorController]. */ +/** A provider of [NotificationTransitionAnimatorController]. */ class NotificationLaunchAnimatorControllerProvider( private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, @@ -42,8 +42,8 @@ class NotificationLaunchAnimatorControllerProvider( fun getAnimatorController( notification: ExpandableNotificationRow, onFinishAnimationCallback: Runnable? = null - ): NotificationLaunchAnimatorController { - return NotificationLaunchAnimatorController( + ): NotificationTransitionAnimatorController { + return NotificationTransitionAnimatorController( notificationLaunchAnimationInteractor, notificationListContainer, headsUpManager, @@ -55,18 +55,18 @@ class NotificationLaunchAnimatorControllerProvider( } /** - * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance - * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a - * notification expanding into an opening window. + * An [ActivityTransitionAnimator.Controller] that animates an [ExpandableNotificationRow]. An + * instance of this class can be passed to [ActivityTransitionAnimator.startIntentWithAnimation] to + * animate a notification expanding into an opening window. */ -class NotificationLaunchAnimatorController( +class NotificationTransitionAnimatorController( private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, private val onFinishAnimationCallback: Runnable? -) : ActivityLaunchAnimator.Controller { +) : ActivityTransitionAnimator.Controller { companion object { const val ANIMATION_DURATION_TOP_ROUNDING = 100L @@ -140,7 +140,7 @@ class NotificationLaunchAnimatorController( } override fun onIntentStarted(willAnimate: Boolean) { - if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { + if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)") } notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate) @@ -173,8 +173,8 @@ class NotificationLaunchAnimatorController( headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate) } - override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { - if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { + override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) { + if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { Log.d(TAG, "onLaunchAnimationCancelled()") } @@ -194,7 +194,7 @@ class NotificationLaunchAnimatorController( } override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) { - if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { + if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { Log.d(TAG, "onLaunchAnimationEnd()") } jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index 8b0b9735754b..c8ca63d9de91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -55,10 +55,9 @@ internal constructor( val notifStats = calculateNotifStats(entries) if (FooterViewRefactor.isEnabled) { activeNotificationsInteractor.setNotifStats(notifStats) + } else { + controller.setNotifStats(notifStats) } - // TODO(b/293167744): This shouldn't be done if the footer flag is on, once the silent - // section clear action is handled in the new stack. - controller.setNotifStats(notifStats) if (NotificationIconContainerRefactor.isEnabled || FooterViewRefactor.isEnabled) { renderListInteractor.setRenderedList(entries) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 92b0c048f3fc..2a1ec3e9c64f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -169,11 +169,12 @@ public interface NotificationsModule { /** Provides notification launch animator. */ @Provides @SysUISingleton - static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider( - NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, - NotificationListContainer notificationListContainer, - HeadsUpManager headsUpManager, - InteractionJankMonitor jankMonitor) { + static NotificationLaunchAnimatorControllerProvider + provideNotificationTransitionAnimatorControllerProvider( + NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, + NotificationListContainer notificationListContainer, + HeadsUpManager headsUpManager, + InteractionJankMonitor jankMonitor) { return new NotificationLaunchAnimatorControllerProvider( notificationLaunchAnimationInteractor, notificationListContainer, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt index b22e9fd2fb17..0dbc8c024c5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt @@ -102,6 +102,18 @@ constructor( .distinctUntilChanged() .flowOn(backgroundDispatcher) + val hasClearableAlertingNotifications: Flow<Boolean> = + repository.notifStats + .map { it.hasClearableAlertingNotifs } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + + val hasNonClearableSilentNotifications: Flow<Boolean> = + repository.notifStats + .map { it.hasNonClearableSilentNotifs } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + fun setNotifStats(notifStats: NotifStats) { repository.notifStats.value = notifStats } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt index 22ce4f11b661..27f2810efb55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import android.util.Log -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository import javax.inject.Inject @@ -33,14 +33,14 @@ constructor(private val repository: NotificationLaunchAnimationRepository) { * Emits true if an animation that expands a notification object into an opening window is * running and false otherwise. * - * See [com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController]. + * See [com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController]. */ val isLaunchAnimationRunning: StateFlow<Boolean> get() = repository.isLaunchAnimationRunning /** Sets whether the notification expansion launch animation is currently running. */ fun setIsLaunchAnimationRunning(running: Boolean) { - if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { + if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { Log.d(TAG, "setIsLaunchAnimationRunning(running=$running)") } repository.isLaunchAnimationRunning.value = running diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index aca8b64c05d2..d7fe36f75418 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -223,7 +223,8 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting // info, and NotificationLockscreenUserManagerImpl is already listening for updates // to those - entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET + entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility == + VISIBILITY_SECRET } else { entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5eeb0665da0e..5686c08e1dea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -87,7 +87,7 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationFadeAware; -import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; +import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -2343,7 +2343,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // top. Otherwise, we just take the top directly. float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( params.getProgress(0, - NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); + NotificationTransitionAnimatorController + .ANIMATION_DURATION_TOP_ROUNDING)); int startTop = params.getStartNotificationTop(); top = (int) Math.min(MathUtils.lerp(startTop, params.getTop(), expandProgress), startTop); 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 42b56e791f77..aa9d3b23e47b 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 @@ -101,7 +101,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; -import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; +import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; @@ -233,7 +233,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>(); private final ArrayList<View> mSwipedOutViews = new ArrayList<>(); private NotificationStackSizeCalculator mNotificationStackSizeCalculator; - private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); + private final StackStateAnimator mStateAnimator; private boolean mAnimationsEnabled; private boolean mChangePositionInProgress; private boolean mChildTransferInProgress; @@ -670,6 +670,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mExpandHelper.setScrollAdapter(mScrollAdapter); mStackScrollAlgorithm = createStackScrollAlgorithm(context); + mStateAnimator = new StackStateAnimator(context, this); mShouldDrawNotificationBackground = res.getBoolean(R.bool.config_drawNotificationBackground); setOutlineProvider(mOutlineProvider); @@ -1083,6 +1084,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height); mStackScrollAlgorithm.initView(context); + mStateAnimator.initView(context); mAmbientState.reload(context); mPaddingBetweenElements = Math.max(1, res.getDimensionPixelSize(R.dimen.notification_divider_height)); @@ -1829,8 +1831,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private ExpandableView getChildAtPosition(float touchX, float touchY) { - return getChildAtPosition( - touchX, touchY, true /* requireMinHeight */, true /* ignoreDecors */); + return getChildAtPosition(touchX, touchY, true /* requireMinHeight */, + true /* ignoreDecors */, true /* ignoreWidth */); } /** @@ -1840,10 +1842,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param touchY the y coordinate * @param requireMinHeight Whether a minimum height is required for a child to be returned. * @param ignoreDecors Whether decors can be returned + * @param ignoreWidth Whether we should ignore the width of the child * @return the child at the given location. */ - ExpandableView getChildAtPosition(float touchX, float touchY, - boolean requireMinHeight, boolean ignoreDecors) { + ExpandableView getChildAtPosition(float touchX, float touchY, boolean requireMinHeight, + boolean ignoreDecors, boolean ignoreWidth) { // find the view under the pointer, accounting for GONE views final int count = getChildCount(); for (int childIdx = 0; childIdx < count; childIdx++) { @@ -1859,8 +1862,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and // camera affordance). - int left = 0; - int right = getWidth(); + int left = ignoreWidth ? 0 : slidingChild.getLeft(); + int right = ignoreWidth ? getWidth() : slidingChild.getRight(); if ((bottom - top >= mMinInteractionHeight || !requireMinHeight) && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) { @@ -3583,8 +3586,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public boolean onTouchEvent(MotionEvent ev) { if (mTouchHandler != null) { boolean touchHandled = mTouchHandler.onTouchEvent(ev); - if (SceneContainerFlag.isEnabled() || touchHandled) { - return touchHandled; + if (SceneContainerFlag.isEnabled()) { + if (getChildAtPosition( + mInitialTouchX, mInitialTouchY, true, true, false) == null) { + // If scene container is enabled, any touch that we are handling that is not on + // a child view should be handled by scene container instead. + return false; + } else { + // If scene container is enabled, any touch that we are handling that is not on + // a child view should be handled by scene container instead. + return touchHandled; + } + } else if (touchHandled) { + return true; } } @@ -4025,7 +4039,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final int y = (int) ev.getY(); mScrolledToTopOnFirstDown = mScrollAdapter.isScrolledToTop(); final ExpandableView childAtTouchPos = getChildAtPosition( - ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */); + ev.getX(), y, false /* requireMinHeight */, + false /* ignoreDecors */, true /* ignoreWidth */); if (childAtTouchPos == null) { setIsBeingDragged(false); recycleVelocityTracker(); @@ -5375,23 +5390,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable && (!hasClipBounds || mTmpRect.height() > 0); } - private boolean shouldHideParent(View view, @SelectedRows int selection) { - final boolean silentSectionWillBeGone = - !mController.hasNotifications(ROWS_GENTLE, false /* clearable */); - - // The only SectionHeaderView we have is the silent section header. - if (view instanceof SectionHeaderView && silentSectionWillBeGone) { - return true; - } - if (view instanceof ExpandableNotificationRow row) { - if (isVisible(row) && includeChildInClearAll(row, selection)) { - return true; - } - } - return false; - } - - private boolean isChildrenVisible(ExpandableNotificationRow parent) { + /** Whether the group is expanded to show the child notifications, and they are visible. */ + private boolean areChildrenVisible(ExpandableNotificationRow parent) { List<ExpandableNotificationRow> children = parent.getAttachedChildren(); return isVisible(parent) && children != null @@ -5399,18 +5399,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } // Similar to #getRowsToDismissInBackend, but filters for visible views. - private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection) { + private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection, + boolean hideSilentSection) { final int viewCount = getChildCount(); final ArrayList<View> viewsToHide = new ArrayList<>(viewCount); for (int i = 0; i < viewCount; i++) { final View view = getChildAt(i); - if (shouldHideParent(view, selection)) { - viewsToHide.add(view); + if (view instanceof SectionHeaderView) { + // The only SectionHeaderView we have is the silent section header. + if (hideSilentSection) { + viewsToHide.add(view); + } } + if (view instanceof ExpandableNotificationRow parent) { - if (isChildrenVisible(parent)) { + if (isVisible(parent) && includeChildInClearAll(parent, selection)) { + viewsToHide.add(parent); + } + + if (areChildrenVisible(parent)) { for (ExpandableNotificationRow child : parent.getAttachedChildren()) { if (isVisible(child) && includeChildInClearAll(child, selection)) { viewsToHide.add(child); @@ -5448,17 +5457,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** Clear all clearable notifications when the user requests it. */ - public void clearAllNotifications() { - clearNotifications(ROWS_ALL, /* closeShade = */ true); + public void clearAllNotifications(boolean hideSilentSection) { + clearNotifications(ROWS_ALL, /* closeShade = */ true, hideSilentSection); + } + + /** Clear all clearable silent notifications when the user requests it. */ + public void clearSilentNotifications(boolean closeShade, + boolean hideSilentSection) { + clearNotifications(ROWS_GENTLE, closeShade, hideSilentSection); + } + + /** Legacy version of clearNotifications below. Uses the old data source for notif stats. */ + void clearNotifications(@SelectedRows int selection, boolean closeShade) { + FooterViewRefactor.assertInLegacyMode(); + final boolean hideSilentSection = !mController.hasNotifications( + ROWS_GENTLE, false /* clearable */); + clearNotifications(selection, closeShade, hideSilentSection); } /** * Collects a list of visible rows, and animates them away in a staggered fashion as if they * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd. */ - void clearNotifications(@SelectedRows int selection, boolean closeShade) { + void clearNotifications(@SelectedRows int selection, boolean closeShade, + boolean hideSilentSection) { // Animate-swipe all dismissable notifications, then animate the shade closed - final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection); + final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection, + hideSilentSection); final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend = getRowsToDismissInBackend(selection); if (mClearAllListener != null) { @@ -5903,7 +5928,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mRoundedRectClippingBottom); float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( mLaunchAnimationParams.getProgress(0, - NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); + NotificationTransitionAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop, mLaunchAnimationParams.getTop() - absoluteCoords[1], expandProgress), mRoundedRectClippingTop); 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 5fa0624c96df..47daf495b87d 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 @@ -596,7 +596,8 @@ public class NotificationStackScrollLayoutController implements Dumpable { ev.getX(), ev.getY(), true /* requireMinHeight */, - false /* ignoreDecors */); + false /* ignoreDecors */, + true /* ignoreWidth */); if (child instanceof ExpandableNotificationRow row) { ExpandableNotificationRow parent = row.getNotificationParent(); if (parent != null && parent.areChildrenExpanded() @@ -916,7 +917,9 @@ public class NotificationStackScrollLayoutController implements Dumpable { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications()); + if (!FooterViewRefactor.isEnabled()) { + mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications()); + } mGroupExpansionManager.registerGroupExpansionChangeListener( (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); @@ -1523,14 +1526,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { * Return whether there are any clearable notifications */ public boolean hasActiveClearableNotifications(@SelectedRows int selection) { - // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent - // section clear action in the new stack. + FooterViewRefactor.assertInLegacyMode(); return hasNotifications(selection, true /* clearable */); } public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) { - // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent - // section clear action in the new stack. + FooterViewRefactor.assertInLegacyMode(); boolean hasAlertingMatchingClearable = isClearable ? mNotifStats.getHasClearableAlertingNotifs() : mNotifStats.getHasNonClearableAlertingNotifs(); @@ -1681,6 +1682,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { } public void clearSilentNotifications() { + FooterViewRefactor.assertInLegacyMode(); // Leave the shade open if there will be other notifs left over to clear final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); mView.clearNotifications(ROWS_GENTLE, closeShade); @@ -2151,8 +2153,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { private class NotifStackControllerImpl implements NotifStackController { @Override public void setNotifStats(@NonNull NotifStats notifStats) { - // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent - // section clear action in the new stack. + FooterViewRefactor.assertInLegacyMode(); mNotifStats = notifStats; if (!FooterViewRefactor.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index b38d619a4434..ab62ed65716e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.content.Context; import android.util.Property; import android.view.View; @@ -66,9 +67,8 @@ public class StackStateAnimator { public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; private static final int MAX_STAGGER_COUNT = 5; - private final int mGoToFullShadeAppearingTranslation; - @VisibleForTesting - float mHeadsUpAppearStartAboveScreen; + @VisibleForTesting int mGoToFullShadeAppearingTranslation; + @VisibleForTesting float mHeadsUpAppearStartAboveScreen; private final ExpandableViewState mTmpState = new ExpandableViewState(); private final AnimationProperties mAnimationProperties; public NotificationStackScrollLayout mHostLayout; @@ -92,14 +92,9 @@ public class StackStateAnimator { private NotificationShelf mShelf; private StackStateLogger mLogger; - public StackStateAnimator(NotificationStackScrollLayout hostLayout) { + public StackStateAnimator(Context context, NotificationStackScrollLayout hostLayout) { mHostLayout = hostLayout; - // TODO(b/317061579) reload on configuration changes - mGoToFullShadeAppearingTranslation = - hostLayout.getContext().getResources().getDimensionPixelSize( - R.dimen.go_to_full_shade_appearing_translation); - mHeadsUpAppearStartAboveScreen = hostLayout.getContext().getResources() - .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen); + initView(context); mAnimationProperties = new AnimationProperties() { @Override public AnimationFilter getAnimationFilter() { @@ -118,6 +113,21 @@ public class StackStateAnimator { }; } + /** + * Needs to be called on configuration changes, to update cached resource values. + */ + public void initView(Context context) { + updateResources(context); + } + + private void updateResources(Context context) { + mGoToFullShadeAppearingTranslation = + context.getResources().getDimensionPixelSize( + R.dimen.go_to_full_shade_appearing_translation); + mHeadsUpAppearStartAboveScreen = context.getResources() + .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen); + } + protected void setLogger(StackStateLogger logger) { mLogger = logger; } @@ -460,15 +470,8 @@ public class StackStateAnimator { mHeadsUpAppearChildren.add(changingView); mTmpState.copyFrom(changingView.getViewState()); - if (event.headsUpFromBottom) { - // start from the bottom of the screen - mTmpState.setYTranslation( - mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen); - } else { - // start from the top of the screen - mTmpState.setYTranslation( - -mStackTopMargin - mHeadsUpAppearStartAboveScreen); - } + // translate the HUN in from the top, or the bottom of the screen + mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom)); // set the height and the initial position mTmpState.applyToView(changingView); mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y, @@ -512,12 +515,20 @@ public class StackStateAnimator { || event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) { mHeadsUpDisappearChildren.add(changingView); Runnable endRunnable = null; + mTmpState.copyFrom(changingView.getViewState()); if (changingView.getParent() == null) { // This notification was actually removed, so we need to add it // transiently mHostLayout.addTransientView(changingView, 0); changingView.setTransientContainer(mHostLayout); - mTmpState.initFrom(changingView); + if (NotificationsImprovedHunAnimation.isEnabled()) { + // StackScrollAlgorithm cannot find this view because it has been removed + // from the NSSL. To correctly translate the view to the top or bottom of + // the screen (where it animated from), we need to update its translation. + mTmpState.setYTranslation( + getHeadsUpYTranslationStart(event.headsUpFromBottom) + ); + } endRunnable = changingView::removeFromTransientContainer; } @@ -565,16 +576,19 @@ public class StackStateAnimator { changingView.setInRemovalAnimation(true); }; } - if (NotificationsImprovedHunAnimation.isEnabled()) { - mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y, - Interpolators.FAST_OUT_SLOW_IN_REVERSE); - } long removeAnimationDelay = changingView.performRemoveAnimation( ANIMATION_DURATION_HEADS_UP_DISAPPEAR, 0, 0.0f, true /* isHeadsUpAppear */, startAnimation, postAnimation, getGlobalAnimationFinishedListener()); mAnimationProperties.delay += removeAnimationDelay; + if (NotificationsImprovedHunAnimation.isEnabled()) { + mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR; + mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y, + Interpolators.FAST_OUT_SLOW_IN_REVERSE); + mAnimationProperties.getAnimationFilter().animateY = true; + mTmpState.animateTo(changingView, mAnimationProperties); + } } else if (endRunnable != null) { endRunnable.run(); } @@ -585,6 +599,15 @@ public class StackStateAnimator { return needsCustomAnimation; } + private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) { + if (headsUpFromBottom) { + // start from the bottom of the screen + return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen; + } + // start from the top of the screen + return -mStackTopMargin - mHeadsUpAppearStartAboveScreen; + } + public void animateOverScrollToAmount(float targetAmount, final boolean onTop, final boolean isRubberbanded) { final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index 883aa9ba668b..6b30393ebc42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -29,6 +29,8 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.notification.NotificationActivityStarter +import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController +import com.android.systemui.statusbar.notification.dagger.SilentHeader import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder @@ -51,10 +53,14 @@ import java.util.Optional import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */ @@ -71,6 +77,7 @@ constructor( private val nicBinder: NotificationIconContainerShelfViewBinder, // Using a provider to avoid a circular dependency. private val notificationActivityStarter: Provider<NotificationActivityStarter>, + @SilentHeader private val silentHeaderController: SectionHeaderController, private val viewModel: NotificationListViewModel, ) { @@ -89,9 +96,14 @@ constructor( bindHideList(viewController, viewModel, hiderTracker) if (FooterViewRefactor.isEnabled) { - launch { reinflateAndBindFooter(view) } + val hasNonClearableSilentNotifications: StateFlow<Boolean> = + viewModel.hasNonClearableSilentNotifications.stateIn(this) + launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) } launch { bindEmptyShade(view) } launch { + bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications) + } + launch { viewModel.isImportantForAccessibility.collect { isImportantForAccessibility -> view.setImportantForAccessibilityYesNo(isImportantForAccessibility) @@ -114,7 +126,10 @@ constructor( ) } - private suspend fun reinflateAndBindFooter(parentView: NotificationStackScrollLayout) { + private suspend fun reinflateAndBindFooter( + parentView: NotificationStackScrollLayout, + hasNonClearableSilentNotifications: StateFlow<Boolean> + ) { viewModel.footer.getOrNull()?.let { footerViewModel -> // The footer needs to be re-inflated every time the theme or the font size changes. configuration @@ -127,7 +142,12 @@ constructor( .collectLatest { footerView: FooterView -> traceAsync("bind FooterView") { parentView.setFooterView(footerView) - bindFooter(footerView, footerViewModel, parentView) + bindFooter( + footerView, + footerViewModel, + parentView, + hasNonClearableSilentNotifications + ) } } } @@ -139,7 +159,8 @@ constructor( private suspend fun bindFooter( footerView: FooterView, footerViewModel: FooterViewModel, - parentView: NotificationStackScrollLayout + parentView: NotificationStackScrollLayout, + hasNonClearableSilentNotifications: StateFlow<Boolean> ): Unit = coroutineScope { val disposableHandle = FooterViewBinder.bindWhileAttached( @@ -147,7 +168,12 @@ constructor( footerViewModel, clearAllNotifications = { metricsLogger.action(MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES) - parentView.clearAllNotifications() + clearAllNotifications( + parentView, + // Hide the silent section header (if present) if there will be + // no remaining silent notifications upon clearing. + hideSilentSection = !hasNonClearableSilentNotifications.value, + ) }, launchNotificationSettings = { view -> notificationActivityStarter @@ -187,6 +213,45 @@ constructor( } } + private suspend fun bindSilentHeaderClickListener( + parentView: NotificationStackScrollLayout, + hasNonClearableSilentNotifications: StateFlow<Boolean>, + ): Unit = coroutineScope { + val hasClearableAlertingNotifications: StateFlow<Boolean> = + viewModel.hasClearableAlertingNotifications.stateIn(this) + silentHeaderController.setOnClearSectionClickListener { + clearSilentNotifications( + view = parentView, + // Leave the shade open if there will be other notifs left over to clear. + closeShade = !hasClearableAlertingNotifications.value, + // Hide the silent section header itself, if there will be no remaining silent + // notifications upon clearing. + hideSilentSection = !hasNonClearableSilentNotifications.value, + ) + } + try { + awaitCancellation() + } finally { + silentHeaderController.setOnClearSectionClickListener {} + } + } + + private fun clearAllNotifications( + view: NotificationStackScrollLayout, + hideSilentSection: Boolean, + ) { + metricsLogger.action(MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES) + view.clearAllNotifications(hideSilentSection) + } + + private fun clearSilentNotifications( + view: NotificationStackScrollLayout, + closeShade: Boolean, + hideSilentSection: Boolean + ) { + view.clearSilentNotifications(closeShade, hideSilentSection) + } + private suspend fun bindLogger(view: NotificationStackScrollLayout) { if (NotificationsLiveDataStoreRefactor.isEnabled) { viewModel.logger.getOrNull()?.let { viewModel -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index a6c658676687..6321820b0733 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -54,7 +52,6 @@ constructor( val logger: Optional<NotificationLoggerViewModel>, activeNotificationsInteractor: ActiveNotificationsInteractor, keyguardInteractor: KeyguardInteractor, - keyguardTransitionInteractor: KeyguardTransitionInteractor, powerInteractor: PowerInteractor, remoteInputInteractor: RemoteInputInteractor, seenNotificationsInteractor: SeenNotificationsInteractor, @@ -74,11 +71,9 @@ constructor( } else { combine( activeNotificationsInteractor.areAnyNotificationsPresent, - keyguardTransitionInteractor.isFinishedInStateWhere { - KeyguardState.lockscreenVisibleInState(it) - } - ) { hasNotifications, isOnKeyguard -> - hasNotifications || !isOnKeyguard + isShowingOnLockscreen, + ) { hasNotifications, isShowingOnLockscreen -> + hasNotifications || !isShowingOnLockscreen } .distinctUntilChanged() } @@ -91,26 +86,17 @@ constructor( combine( activeNotificationsInteractor.areAnyNotificationsPresent, shadeInteractor.isQsFullscreen, - // TODO(b/293167744): It looks like we're essentially trying to check the same - // things for the empty shade visibility as we do for the footer, just in a - // slightly different way. We should change this so we also check - // statusBarState and isAwake instead of specific keyguard transitions. - keyguardTransitionInteractor.isInTransitionToState(KeyguardState.AOD).onStart { - emit(false) - }, - keyguardTransitionInteractor - .isFinishedInState(KeyguardState.PRIMARY_BOUNCER) - .onStart { emit(false) } - ) { hasNotifications, isQsFullScreen, transitioningToAOD, isBouncerShowing -> - !hasNotifications && - !isQsFullScreen && - // Hide empty shade view when in transition to AOD. - // That avoids "No Notifications" blinking when transitioning to AOD. - // For more details, see b/228790482. - !transitioningToAOD && - // Don't show any notification content if the bouncer is showing. See - // b/267060171. - !isBouncerShowing + isShowingOnLockscreen, + ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen -> + when { + hasNotifications -> false + isQsFullScreen -> false + // Do not show the empty shade if the lockscreen is visible (including AOD + // b/228790482 and bouncer b/267060171), except if the shade is opened on + // top. + isShowingOnLockscreen -> false + else -> true + } } .distinctUntilChanged() } @@ -123,52 +109,47 @@ constructor( combine( activeNotificationsInteractor.areAnyNotificationsPresent, userSetupInteractor.isUserSetUp, - keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD }, + isShowingOnLockscreen, shadeInteractor.qsExpansion, shadeInteractor.isQsFullscreen, - powerInteractor.isAsleep, remoteInputInteractor.isRemoteInputActive, shadeInteractor.shadeExpansion.map { it == 0f } ) { hasNotifications, isUserSetUp, - isOnKeyguard, + isShowingOnLockscreen, qsExpansion, qsFullScreen, - isAsleep, isRemoteInputActive, isShadeClosed -> - Pair( - // Should the footer be visible? - when { - !hasNotifications -> false - // Hide the footer until the user setup is complete, to prevent access - // to settings (b/193149550). - !isUserSetUp -> false - // Do not show the footer if the lockscreen is visible (incl. AOD), - // except if the shade is opened on top. See also b/219680200. - isOnKeyguard -> false - // Make sure we're not showing the footer in the transition to AOD while - // going to sleep (b/190227875). The StatusBarState is unfortunately not - // updated quickly enough when the power button is pressed, so this is - // necessary in addition to the isOnKeyguard check. - isAsleep -> false - // Do not show the footer if quick settings are fully expanded (except - // for the foldable split shade view). See b/201427195 && b/222699879. - qsExpansion == 1f && qsFullScreen -> false - // Hide the footer if remote input is active (i.e. user is replying to a - // notification). See b/75984847. - isRemoteInputActive -> false - // Never show the footer if the shade is collapsed (e.g. when HUNing). - isShadeClosed -> false - else -> true - }, - // This could in theory be in the .sample below, but it tends to be - // inconsistent, so we're passing it on to make sure we have the same state. - isOnKeyguard - ) + // A pair of (visible, canAnimate) + when { + !hasNotifications -> Pair(false, true) + // Hide the footer until the user setup is complete, to prevent access + // to settings (b/193149550). + !isUserSetUp -> Pair(false, true) + // Do not show the footer if the lockscreen is visible (incl. AOD), + // except if the shade is opened on top. See also b/219680200. + // Do not animate, as that makes the footer appear briefly when + // transitioning between the shade and keyguard. + isShowingOnLockscreen -> Pair(false, false) + // Do not show the footer if quick settings are fully expanded (except + // for the foldable split shade view). See b/201427195 && b/222699879. + qsExpansion == 1f && qsFullScreen -> Pair(false, true) + // Hide the footer if remote input is active (i.e. user is replying to a + // notification). See b/75984847. + isRemoteInputActive -> Pair(false, true) + // Never show the footer if the shade is collapsed (e.g. when HUNing). + isShadeClosed -> Pair(false, false) + else -> Pair(true, true) + } } - .distinctUntilChanged() + .distinctUntilChanged( + // Equivalent unless visibility changes + areEquivalent = { a: Pair<Boolean, Boolean>, b: Pair<Boolean, Boolean> -> + a.first == b.first + } + ) // Should we animate the visibility change? .sample( // TODO(b/322167853): This check is currently duplicated in FooterViewModel, @@ -179,17 +160,40 @@ constructor( ::Pair ) .onStart { emit(Pair(false, false)) } - ) { (visible, isOnKeyguard), (isShadeFullyExpanded, animationsEnabled) -> + ) { (visible, canAnimate), (isShadeFullyExpanded, animationsEnabled) -> // Animate if the shade is interactive, but NOT on the lockscreen. Having // animations enabled while on the lockscreen makes the footer appear briefly // when transitioning between the shade and keyguard. - val shouldAnimate = isShadeFullyExpanded && animationsEnabled && !isOnKeyguard + val shouldAnimate = isShadeFullyExpanded && animationsEnabled && canAnimate AnimatableEvent(visible, shouldAnimate) } .toAnimatedValueFlow() } } + private val isShowingOnLockscreen: Flow<Boolean> by lazy { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { + flowOf(false) + } else { + combine( + // Non-notification UI elements of the notification list should not be visible + // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on + // top. See b/219680200 for the footer and b/228790482, b/267060171 for the + // empty shade. + // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState + // entirely, so this will have to be replaced at some point. + keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD }, + // The StatusBarState is unfortunately not updated quickly enough when the power + // button is pressed, so this is necessary in addition to the KEYGUARD check to + // cover the transition to AOD while going to sleep (b/190227875). + powerInteractor.isAsleep, + ) { (isOnKeyguard, isAsleep) -> + isOnKeyguard || isAsleep + } + .distinctUntilChanged() + } + } + // TODO(b/308591475): This should be tracked separately by the empty shade. val areNotificationsHiddenInShade: Flow<Boolean> by lazy { if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { @@ -207,4 +211,20 @@ constructor( seenNotificationsInteractor.hasFilteredOutSeenNotifications } } + + val hasClearableAlertingNotifications: Flow<Boolean> by lazy { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { + flowOf(false) + } else { + activeNotificationsInteractor.hasClearableAlertingNotifications + } + } + + val hasNonClearableSilentNotifications: Flow<Boolean> by lazy { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { + flowOf(false) + } else { + activeNotificationsInteractor.hasNonClearableSilentNotifications + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index b49af0e64772..270b94b1893e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -30,9 +30,9 @@ import android.view.View import android.view.WindowManager import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper -import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter -import com.android.systemui.animation.DelegateLaunchAnimatorController +import com.android.systemui.animation.ActivityTransitionAnimator +import com.android.systemui.animation.ActivityTransitionAnimator.PendingIntentStarter +import com.android.systemui.animation.DelegateTransitionAnimatorController import com.android.systemui.assist.AssistManager import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent import com.android.systemui.dagger.SysUISingleton @@ -75,7 +75,7 @@ constructor( private val shadeAnimationInteractor: ShadeAnimationInteractor, private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>, private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, - private val activityLaunchAnimator: ActivityLaunchAnimator, + private val activityTransitionAnimator: ActivityTransitionAnimator, private val context: Context, @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, @@ -127,7 +127,7 @@ constructor( override fun startPendingIntentDismissingKeyguard( intent: PendingIntent, intentSentUiThreadCallback: Runnable?, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, ) { activityStarterInternal.startPendingIntentDismissingKeyguard( intent = intent, @@ -139,7 +139,7 @@ constructor( override fun startPendingIntentMaybeDismissingKeyguard( intent: PendingIntent, intentSentUiThreadCallback: Runnable?, - animationController: ActivityLaunchAnimator.Controller? + animationController: ActivityTransitionAnimator.Controller? ) { activityStarterInternal.startPendingIntentDismissingKeyguard( intent = intent, @@ -209,7 +209,7 @@ constructor( override fun startActivity( intent: Intent, dismissShade: Boolean, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, showOverLockscreenWhenLocked: Boolean, ) { activityStarterInternal.startActivity( @@ -222,7 +222,7 @@ constructor( override fun startActivity( intent: Intent, dismissShade: Boolean, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, showOverLockscreenWhenLocked: Boolean, userHandle: UserHandle?, ) { @@ -245,7 +245,7 @@ constructor( override fun postStartActivityDismissingKeyguard( intent: PendingIntent, - animationController: ActivityLaunchAnimator.Controller? + animationController: ActivityTransitionAnimator.Controller? ) { postOnUiThread { activityStarterInternal.startPendingIntentDismissingKeyguard( @@ -268,7 +268,7 @@ constructor( override fun postStartActivityDismissingKeyguard( intent: Intent, delay: Int, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, ) { postOnUiThread(delay) { activityStarterInternal.startActivityDismissingKeyguard( @@ -283,7 +283,7 @@ constructor( override fun postStartActivityDismissingKeyguard( intent: Intent, delay: Int, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, customMessage: String?, ) { postOnUiThread(delay) { @@ -342,7 +342,7 @@ constructor( disallowEnterPictureInPictureWhileLaunching: Boolean, callback: ActivityStarter.Callback?, flags: Int, - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, userHandle: UserHandle?, ) { activityStarterInternal.startActivityDismissingKeyguard( @@ -430,7 +430,7 @@ constructor( disallowEnterPictureInPictureWhileLaunching: Boolean = false, callback: ActivityStarter.Callback? = null, flags: Int = 0, - animationController: ActivityLaunchAnimator.Controller? = null, + animationController: ActivityTransitionAnimator.Controller? = null, userHandle: UserHandle? = null, customMessage: String? = null, ) { @@ -464,7 +464,7 @@ constructor( intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP intent.addFlags(flags) val result = intArrayOf(ActivityManager.START_CANCELED) - activityLaunchAnimator.startIntentWithAnimation( + activityTransitionAnimator.startIntentWithAnimation( animController, animate, intent.getPackage() @@ -552,7 +552,7 @@ constructor( intent: PendingIntent, intentSentUiThreadCallback: Runnable? = null, associatedView: View? = null, - animationController: ActivityLaunchAnimator.Controller? = null, + animationController: ActivityTransitionAnimator.Controller? = null, showOverLockscreen: Boolean = false, ) { val animationController = @@ -602,7 +602,7 @@ constructor( val collapse = !animate val runnable = Runnable { try { - activityLaunchAnimator.startPendingIntentWithAnimation( + activityTransitionAnimator.startPendingIntentWithAnimation( controller, animate, intent.creatorPackage, @@ -670,7 +670,7 @@ constructor( fun startActivity( intent: Intent, dismissShade: Boolean = false, - animationController: ActivityLaunchAnimator.Controller? = null, + animationController: ActivityTransitionAnimator.Controller? = null, showOverLockscreenWhenLocked: Boolean = false, userHandle: UserHandle? = null, ) { @@ -698,7 +698,7 @@ constructor( showOverLockscreenWhenLocked ) == true - var controller: ActivityLaunchAnimator.Controller? = null + var controller: ActivityTransitionAnimator.Controller? = null if (animate) { // Wrap the animation controller to dismiss the shade and set // mIsLaunchingActivityOverLockscreen during the animation. @@ -721,7 +721,7 @@ constructor( centralSurfaces?.awakenDreams() } - activityLaunchAnimator.startIntentWithAnimation( + activityTransitionAnimator.startIntentWithAnimation( controller, animate, intent.getPackage(), @@ -815,7 +815,7 @@ constructor( } /** - * Return a [ActivityLaunchAnimator.Controller] wrapping `animationController` so that: + * Return a [ActivityTransitionAnimator.Controller] wrapping `animationController` so that: * - if it launches in the notification shade window and `dismissShade` is true, then the * shade will be instantly dismissed at the end of the animation. * - if it launches in status bar window, it will make the status bar window match the @@ -830,15 +830,15 @@ constructor( * @param isLaunchForActivity whether the launch is for an activity. */ private fun wrapAnimationControllerForShadeOrStatusBar( - animationController: ActivityLaunchAnimator.Controller?, + animationController: ActivityTransitionAnimator.Controller?, dismissShade: Boolean, isLaunchForActivity: Boolean, - ): ActivityLaunchAnimator.Controller? { + ): ActivityTransitionAnimator.Controller? { if (animationController == null) { return null } val rootView = animationController.transitionContainer.rootView - val controllerFromStatusBar: Optional<ActivityLaunchAnimator.Controller> = + val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> = statusBarWindowController.wrapAnimationControllerIfInStatusBar( rootView, animationController @@ -851,7 +851,7 @@ constructor( // If the view is not in the status bar, then we are animating a view in the shade. // We have to make sure that we collapse it when the animation ends or is cancelled. if (dismissShade) { - return StatusBarLaunchAnimatorController( + return StatusBarTransitionAnimatorController( animationController, shadeViewControllerLazy.get(), shadeAnimationInteractor, @@ -870,10 +870,10 @@ constructor( * lockscreen, the correct flags are set for it to be occluded. */ private fun wrapAnimationControllerForLockscreen( - animationController: ActivityLaunchAnimator.Controller? - ): ActivityLaunchAnimator.Controller? { + animationController: ActivityTransitionAnimator.Controller? + ): ActivityTransitionAnimator.Controller? { return animationController?.let { - object : DelegateLaunchAnimatorController(it) { + object : DelegateTransitionAnimatorController(it) { override fun onIntentStarted(willAnimate: Boolean) { delegate.onIntentStarted(willAnimate) if (willAnimate) { @@ -912,7 +912,9 @@ constructor( delegate.onTransitionAnimationEnd(isExpandingFullyAbove) } - override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { + override fun onTransitionAnimationCancelled( + newKeyguardOccludedState: Boolean? + ) { if (newKeyguardOccludedState != null) { keyguardViewMediatorLazy .get() @@ -925,7 +927,7 @@ constructor( // collapse the shade (or at least run the // post collapse // runnables) later on. centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) - delegate.onLaunchAnimationCancelled(newKeyguardOccludedState) + delegate.onTransitionAnimationCancelled(newKeyguardOccludedState) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 40194361e12b..9052409b4d8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -38,7 +38,7 @@ import androidx.lifecycle.LifecycleOwner; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.systemui.Dumpable; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.display.data.repository.DisplayMetricsRepository; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; @@ -334,6 +334,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { /** * Gets an animation controller from a notification row. */ - ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification( + ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification( ExpandableNotificationRow associatedView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt index 60dfaa790ec9..8af7ee8389e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt @@ -20,7 +20,7 @@ import android.content.Intent import android.view.MotionEvent import androidx.lifecycle.LifecycleRegistry import com.android.keyguard.AuthKeyguardMessageArea -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.navigationbar.NavigationBarView import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.qs.QSPanelController @@ -99,5 +99,5 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces { override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {} override fun getAnimatorControllerFromNotification( associatedView: ExpandableNotificationRow?, - ): ActivityLaunchAnimator.Controller? = null + ): ActivityTransitionAnimator.Controller? = null } 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 3e7089c70deb..4a5411115599 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -30,6 +30,7 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import static com.android.systemui.Flags.lightRevealMigration; import static com.android.systemui.Flags.newAodTransition; import static com.android.systemui.Flags.predictiveBackSysui; +import static com.android.systemui.Flags.truncatedStatusBarIconsFix; import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -115,7 +116,7 @@ import com.android.systemui.EventLogTags; import com.android.systemui.InitController; import com.android.systemui.Prefs; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.back.domain.interactor.BackActionInteractor; import com.android.systemui.biometrics.AuthRippleController; @@ -576,7 +577,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; - private final ActivityLaunchAnimator mActivityLaunchAnimator; + private final ActivityTransitionAnimator mActivityTransitionAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; private final Lazy<NotificationPresenter> mPresenterLazy; private final Lazy<NotificationActivityStarter> mNotificationActivityStarterLazy; @@ -655,7 +656,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // Lazys due to b/298099682. Lazy<NotificationPresenter> notificationPresenterLazy, Lazy<NotificationActivityStarter> notificationActivityStarterLazy, - NotificationLaunchAnimatorControllerProvider notifLaunchAnimatorControllerProvider, + NotificationLaunchAnimatorControllerProvider notifTransitionAnimatorControllerProvider, DozeParameters dozeParameters, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, @@ -694,7 +695,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Main MessageRouter messageRouter, WallpaperManager wallpaperManager, Optional<StartingSurface> startingSurfaceOptional, - ActivityLaunchAnimator activityLaunchAnimator, + ActivityTransitionAnimator activityTransitionAnimator, DeviceStateManager deviceStateManager, WiredChargingRippleController wiredChargingRippleController, IDreamManager dreamManager, @@ -761,7 +762,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mNotifListContainer = mStackScrollerController.getNotificationListContainer(); mPresenterLazy = notificationPresenterLazy; mNotificationActivityStarterLazy = notificationActivityStarterLazy; - mNotificationAnimationProvider = notifLaunchAnimatorControllerProvider; + mNotificationAnimationProvider = notifTransitionAnimatorControllerProvider; mDozeServiceHost = dozeServiceHost; mPowerManager = powerManager; mDozeParameters = dozeParameters; @@ -816,7 +817,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { shadeExpansionListener.onPanelExpansionChanged(currentState); mActivityIntentHelper = new ActivityIntentHelper(mContext); - mActivityLaunchAnimator = activityLaunchAnimator; + mActivityTransitionAnimator = activityTransitionAnimator; // TODO(b/190746471): Find a better home for this. DateTimeView.setReceiverHandler(timeTickHandler); @@ -1424,8 +1425,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback); - mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener); + mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback); + mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener); mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); mStackScrollerController.setNotificationActivityStarter( mNotificationActivityStarterLazy.get()); @@ -1906,10 +1907,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mQSPanelController.updateResources(); } - if (mStatusBarWindowController != null) { - mStatusBarWindowController.refreshStatusBarHeight(); + if (!truncatedStatusBarIconsFix()) { + if (mStatusBarWindowController != null) { + mStatusBarWindowController.refreshStatusBarHeight(); + } } - if (mShadeSurface != null) { mShadeSurface.updateResources(); } @@ -3176,8 +3178,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; - private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback = - new ActivityLaunchAnimator.Callback() { + private final ActivityTransitionAnimator.Callback mActivityTransitionAnimatorCallback = + new ActivityTransitionAnimator.Callback() { @Override public boolean isOnKeyguard() { return mKeyguardStateController.isShowing(); @@ -3205,15 +3207,15 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; - private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener = - new ActivityLaunchAnimator.Listener() { + private final ActivityTransitionAnimator.Listener mActivityTransitionAnimatorListener = + new ActivityTransitionAnimator.Listener() { @Override - public void onLaunchAnimationStart() { + public void onTransitionAnimationStart() { mKeyguardViewMediator.setBlursDisabledForAppLaunch(true); } @Override - public void onLaunchAnimationEnd() { + public void onTransitionAnimationEnd() { mKeyguardViewMediator.setBlursDisabledForAppLaunch(false); } }; @@ -3267,7 +3269,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification( + public ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification( ExpandableNotificationRow associatedView) { return mNotificationAnimationProvider.getAnimatorController(associatedView); } 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 cb7bc256504e..45bdae83bbd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.truncatedStatusBarIconsFix; + import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; @@ -41,6 +43,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder; import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel; import com.android.systemui.util.leak.RotationUtils; @@ -50,6 +53,7 @@ import java.util.Objects; public class PhoneStatusBarView extends FrameLayout { private static final String TAG = "PhoneStatusBarView"; private final StatusBarContentInsetsProvider mContentInsetsProvider; + private final StatusBarWindowController mStatusBarWindowController; private DarkReceiver mBattery; private Clock mClock; @@ -72,6 +76,7 @@ public class PhoneStatusBarView extends FrameLayout { public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class); + mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); } void setTouchEventHandler(Gefingerpoken handler) { @@ -101,6 +106,9 @@ public class PhoneStatusBarView extends FrameLayout { Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock); if (updateDisplayParameters()) { updateLayoutForCutout(); + if (truncatedStatusBarIconsFix()) { + updateWindowHeight(); + } } } @@ -124,6 +132,9 @@ public class PhoneStatusBarView extends FrameLayout { if (updateDisplayParameters()) { updateLayoutForCutout(); requestLayout(); + if (truncatedStatusBarIconsFix()) { + updateWindowHeight(); + } } } @@ -279,4 +290,8 @@ public class PhoneStatusBarView extends FrameLayout { insets.right, getPaddingBottom()); } + + private void updateWindowHeight() { + mStatusBarWindowController.refreshStatusBarHeight(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 3ad60d946c1c..7f7eb04f9970 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -482,20 +482,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (KeyguardWmStateRefactor.isEnabled()) { // Show the keyguard views whenever we've told WM that the lockscreen is visible. - mShadeViewController.postToView(() -> - collectFlow( - getViewRootImpl().getView(), - combineFlows( - mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(), - mSurfaceBehindInteractor.get().isAnimatingSurface(), - (lockscreenVis, animatingSurface) -> - // TODO(b/322546110): Waiting until we're not animating the - // surface is a workaround to avoid jank. We should actually - // fix the source of the jank, and then hide the keyguard - // view without waiting for the animation to end. - lockscreenVis || animatingSurface - ), - this::consumeShowStatusBarKeyguardView)); + collectFlow( + getViewRootImpl().getView(), + combineFlows( + mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(), + mSurfaceBehindInteractor.get().isAnimatingSurface(), + (lockscreenVis, animatingSurface) -> + // TODO(b/322546110): Waiting until we're not animating the + // surface is a workaround to avoid jank. We should actually + // fix the source of the jank, and then hide the keyguard + // view without waiting for the animation to end. + lockscreenVis || animatingSurface + ), + this::consumeShowStatusBarKeyguardView); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 4ee061d05a3b..4fd33ba458d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -52,7 +52,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -125,7 +125,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationPresenter mPresenter; private final ShadeViewController mShadeViewController; private final NotificationShadeWindowController mNotificationShadeWindowController; - private final ActivityLaunchAnimator mActivityLaunchAnimator; + private final ActivityTransitionAnimator mActivityTransitionAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; private final PowerInteractor mPowerInteractor; private final UserTracker mUserTracker; @@ -161,7 +161,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationPresenter presenter, ShadeViewController shadeViewController, NotificationShadeWindowController notificationShadeWindowController, - ActivityLaunchAnimator activityLaunchAnimator, + ActivityTransitionAnimator activityTransitionAnimator, ShadeAnimationInteractor shadeAnimationInteractor, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, @@ -194,7 +194,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mOnUserInteractionCallback = onUserInteractionCallback; mPresenter = presenter; mShadeViewController = shadeViewController; - mActivityLaunchAnimator = activityLaunchAnimator; + mActivityTransitionAnimator = activityTransitionAnimator; mNotificationAnimationProvider = notificationAnimationProvider; mPowerInteractor = powerInteractor; mUserTracker = userTracker; @@ -440,15 +440,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean isActivityIntent) { mLogger.logStartNotificationIntent(entry); try { - ActivityLaunchAnimator.Controller animationController = - new StatusBarLaunchAnimatorController( + ActivityTransitionAnimator.Controller animationController = + new StatusBarTransitionAnimatorController( mNotificationAnimationProvider.getAnimatorController(row, null), mShadeViewController, mShadeAnimationInteractor, mShadeController, mNotificationShadeWindowController, isActivityIntent); - mActivityLaunchAnimator.startPendingIntentWithAnimation( + mActivityTransitionAnimator.startPendingIntentWithAnimation( animationController, animate, intent.getCreatorPackage(), @@ -482,8 +482,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public boolean onDismiss() { AsyncTask.execute(() -> { - ActivityLaunchAnimator.Controller animationController = - new StatusBarLaunchAnimatorController( + ActivityTransitionAnimator.Controller animationController = + new StatusBarTransitionAnimatorController( mNotificationAnimationProvider.getAnimatorController(row), mShadeViewController, mShadeAnimationInteractor, @@ -491,7 +491,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mNotificationShadeWindowController, true /* isActivityIntent */); - mActivityLaunchAnimator.startIntentWithAnimation( + mActivityTransitionAnimator.startIntentWithAnimation( animationController, animate, intent.getPackage(), (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) @@ -528,13 +528,13 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit tsb.addNextIntent(intent); } - ActivityLaunchAnimator.Controller viewController = - ActivityLaunchAnimator.Controller.fromView(view, + ActivityTransitionAnimator.Controller viewController = + ActivityTransitionAnimator.Controller.fromView(view, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON ); - ActivityLaunchAnimator.Controller animationController = + ActivityTransitionAnimator.Controller animationController = viewController == null ? null - : new StatusBarLaunchAnimatorController( + : new StatusBarTransitionAnimatorController( viewController, mShadeViewController, mShadeAnimationInteractor, @@ -542,8 +542,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mNotificationShadeWindowController, true /* isActivityIntent */); - mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, - intent.getPackage(), + mActivityTransitionAnimator.startIntentWithAnimation( + animationController, animate, intent.getPackage(), (adapter) -> tsb.startActivities( getActivityOptions(mDisplayId, adapter), mUserTracker.getUserHandle())); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt index d43f4709a32d..7e907d80d277 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt @@ -1,7 +1,7 @@ package com.android.systemui.statusbar.phone import android.view.View -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.TransitionAnimator import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeViewController @@ -9,17 +9,17 @@ import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor import com.android.systemui.statusbar.NotificationShadeWindowController /** - * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right - * time. + * A [ActivityTransitionAnimator.Controller] that takes care of collapsing the status bar at the + * right time. */ -class StatusBarLaunchAnimatorController( - private val delegate: ActivityLaunchAnimator.Controller, +class StatusBarTransitionAnimatorController( + private val delegate: ActivityTransitionAnimator.Controller, private val shadeViewController: ShadeViewController, private val shadeAnimationInteractor: ShadeAnimationInteractor, private val shadeController: ShadeController, private val notificationShadeWindowController: NotificationShadeWindowController, private val isLaunchForActivity: Boolean = true -) : ActivityLaunchAnimator.Controller by delegate { +) : ActivityTransitionAnimator.Controller by delegate { // Always sync the opening window with the shade, given that we draw a hole punch in the shade // of the same size and position as the opening app to make it visible. override val openingWindowSyncView: View? @@ -39,7 +39,8 @@ class StatusBarLaunchAnimatorController( shadeAnimationInteractor.setIsLaunchingActivity(true) if (!isExpandingFullyAbove) { shadeViewController.collapseWithDuration( - ActivityLaunchAnimator.TIMINGS.totalDuration.toInt()) + ActivityTransitionAnimator.TIMINGS.totalDuration.toInt() + ) } } @@ -58,9 +59,9 @@ class StatusBarLaunchAnimatorController( shadeViewController.applyLaunchAnimationProgress(linearProgress) } - override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { - delegate.onLaunchAnimationCancelled() + override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) { + delegate.onTransitionAnimationCancelled() shadeAnimationInteractor.setIsLaunchingActivity(false) shadeController.onLaunchAnimationCancelled(isLaunchForActivity) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 0bdd1a5b4d5f..a20468f9e3ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -30,7 +30,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.res.R -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main @@ -233,7 +233,7 @@ class OngoingCallController @Inject constructor( logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, - ActivityLaunchAnimator.Controller.fromView( + ActivityTransitionAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java index 21d3fa47f51c..21f1a3dfc23a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java @@ -47,8 +47,8 @@ import android.view.WindowInsets; import android.view.WindowManager; import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.DelegateLaunchAnimatorController; +import com.android.systemui.animation.ActivityTransitionAnimator; +import com.android.systemui.animation.DelegateTransitionAnimatorController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; @@ -188,14 +188,14 @@ public class StatusBarWindowController { * updated animation controller that handles status-bar-related animation details. Returns an * empty optional if the animation is *not* on a view in the status bar. */ - public Optional<ActivityLaunchAnimator.Controller> wrapAnimationControllerIfInStatusBar( - View rootView, ActivityLaunchAnimator.Controller animationController) { + public Optional<ActivityTransitionAnimator.Controller> wrapAnimationControllerIfInStatusBar( + View rootView, ActivityTransitionAnimator.Controller animationController) { if (rootView != mStatusBarWindowView) { return Optional.empty(); } animationController.setTransitionContainer(mLaunchAnimationContainer); - return Optional.of(new DelegateLaunchAnimatorController(animationController) { + return Optional.of(new DelegateTransitionAnimatorController(animationController) { @Override public void onTransitionAnimationStart(boolean isExpandingFullyAbove) { getDelegate().onTransitionAnimationStart(isExpandingFullyAbove); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 3376e232e035..147e158bd393 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -15,6 +15,8 @@ */ package com.android.systemui.theme; +import static com.android.systemui.shared.Flags.enableHomeDelay; + import android.annotation.AnyThread; import android.content.om.FabricatedOverlay; import android.content.om.OverlayIdentifier; @@ -32,6 +34,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.google.android.collect.Lists; @@ -142,6 +145,7 @@ public class ThemeOverlayApplier implements Dumpable { private final Map<String, String> mCategoryToTargetPackage = new ArrayMap<>(); private final OverlayManager mOverlayManager; private final Executor mBgExecutor; + private final Executor mMainExecutor; private final String mLauncherPackage; private final String mThemePickerPackage; @@ -150,9 +154,11 @@ public class ThemeOverlayApplier implements Dumpable { @Background Executor bgExecutor, @Named(ThemeModule.LAUNCHER_PACKAGE) String launcherPackage, @Named(ThemeModule.THEME_PICKER_PACKAGE) String themePickerPackage, - DumpManager dumpManager) { + DumpManager dumpManager, + @Main Executor mainExecutor) { mOverlayManager = overlayManager; mBgExecutor = bgExecutor; + mMainExecutor = mainExecutor; mLauncherPackage = launcherPackage; mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( @@ -184,12 +190,21 @@ public class ThemeOverlayApplier implements Dumpable { /** * Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that * affect sysui will also be applied to the system user. + * + * @param categoryToPackage Overlay packages to be applied + * @param pendingCreation Overlays yet to be created + * @param currentUser Current User ID + * @param managedProfiles Profiles get overlays + * @param onComplete Callback for when resources are ready. Runs in the main thread. */ public void applyCurrentUserOverlays( Map<String, OverlayIdentifier> categoryToPackage, FabricatedOverlay[] pendingCreation, int currentUser, - Set<UserHandle> managedProfiles) { + Set<UserHandle> managedProfiles, + Runnable onComplete + ) { + mBgExecutor.execute(() -> { // Disable all overlays that have not been specified in the user setting. @@ -236,6 +251,10 @@ public class ThemeOverlayApplier implements Dumpable { try { mOverlayManager.commit(transaction.build()); + if (enableHomeDelay() && onComplete != null) { + Log.d(TAG, "Executing onComplete runnable"); + mMainExecutor.execute(onComplete); + } } catch (SecurityException | IllegalStateException e) { Log.e(TAG, "setEnabled failed", e); } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 2b9ad50c1257..585ab72dd963 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -20,6 +20,7 @@ import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; +import static com.android.systemui.shared.Flags.enableHomeDelay; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; @@ -31,6 +32,7 @@ import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE; import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD; +import android.app.ActivityManager; import android.app.UiModeManager; import android.app.WallpaperColors; import android.app.WallpaperManager; @@ -140,6 +142,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { // Current wallpaper colors associated to a user. private final SparseArray<WallpaperColors> mCurrentColors = new SparseArray<>(); private final WallpaperManager mWallpaperManager; + private final ActivityManager mActivityManager; @VisibleForTesting protected ColorScheme mColorScheme; // If fabricated overlays were already created for the current theme. @@ -414,7 +417,8 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { WakefulnessLifecycle wakefulnessLifecycle, JavaAdapter javaAdapter, KeyguardTransitionInteractor keyguardTransitionInteractor, - UiModeManager uiModeManager) { + UiModeManager uiModeManager, + ActivityManager activityManager) { mContext = context; mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET); mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY); @@ -433,6 +437,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mJavaAdapter = javaAdapter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mUiModeManager = uiModeManager; + mActivityManager = activityManager; dumpManager.registerDumpable(TAG, this); } @@ -806,8 +811,16 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { } } + final Runnable onCompleteCallback = !enableHomeDelay() + ? () -> {} + : () -> { + Log.d(TAG, "ThemeHomeDelay: ThemeOverlayController ready"); + mActivityManager.setThemeOverlayReady(true); + }; + if (colorSchemeIsApplied(managedProfiles)) { Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme); + onCompleteCallback.run(); return; } @@ -816,15 +829,19 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { .map(key -> key + " -> " + categoryToPackage.get(key)).collect( Collectors.joining(", "))); } + + FabricatedOverlay[] fOverlays = null; + if (mNeedsOverlayCreation) { mNeedsOverlayCreation = false; - mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[]{ + fOverlays = new FabricatedOverlay[]{ mSecondaryOverlay, mNeutralOverlay, mDynamicOverlay - }, currentUser, managedProfiles); - } else { - mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser, - managedProfiles); + }; } + + mThemeManager.applyCurrentUserOverlays(categoryToPackage, fOverlays, currentUser, + managedProfiles, onCompleteCallback); + } private Style fetchThemeStyleFromSetting() { diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt index 0fb4b43865aa..38b381ac543e 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt @@ -1,6 +1,7 @@ package com.android.systemui.user.domain.interactor import android.annotation.UserIdInt +import android.content.pm.UserInfo import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Flags.refactorGetCurrentUser import com.android.systemui.dagger.SysUISingleton @@ -16,6 +17,9 @@ class SelectedUserInteractor @Inject constructor(private val repository: UserRep /** Flow providing the ID of the currently selected user. */ val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged() + /** Flow providing the [UserInfo] of the currently selected user. */ + val selectedUserInfo = repository.selectedUserInfo + /** * Returns the ID of the currently-selected user. * diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index 0d0a6469d674..46ce5f2623de 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -18,6 +18,7 @@ package com.android.systemui.util.kotlin import android.view.View import androidx.lifecycle.Lifecycle +import androidx.lifecycle.coroutineScope import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -76,6 +77,23 @@ fun <T> collectFlow( } } +/** + * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to + * [LifeCycle.State.CREATED] which is mapped over from the equivalent definition for collecting the + * flow on a view. + */ +@JvmOverloads +fun <T> collectFlow( + lifecycle: Lifecycle, + flow: Flow<T>, + consumer: Consumer<T>, + state: Lifecycle.State = Lifecycle.State.CREATED, +) { + lifecycle.coroutineScope.launch { + lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } } + } +} + fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> { return combine(flow1, flow2, bifunction) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt index 278ffc6b8412..1af5c46cd14b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt @@ -18,6 +18,9 @@ package com.android.systemui.volume.dagger import android.content.Context import android.media.AudioManager +import com.android.settingslib.media.data.repository.SpatializerRepository +import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl +import com.android.settingslib.media.domain.interactor.SpatializerInteractor import com.android.settingslib.volume.data.repository.AudioRepository import com.android.settingslib.volume.data.repository.AudioRepositoryImpl import com.android.settingslib.volume.domain.interactor.AudioModeInteractor @@ -54,5 +57,16 @@ interface AudioModule { @Provides fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor = AudioModeInteractor(repository) + + @Provides + fun provdieSpatializerRepository( + audioManager: AudioManager, + @Background backgroundContext: CoroutineContext, + ): SpatializerRepository = + SpatializerRepositoryImpl(audioManager.spatializer, backgroundContext) + + @Provides + fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor = + SpatializerInteractor(repository) } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index e0228d913977..1d9b90ac67af 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -34,7 +34,7 @@ import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.QuickAccessWalletClientImpl; import android.util.Log; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -236,12 +236,12 @@ public class QuickAccessWalletController { * that too is null, then fall back to {@link WalletActivity}. * * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent. - * @param animationController an {@link ActivityLaunchAnimator.Controller} to provide a + * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a * smooth animation for the activity launch. * @param hasCard whether the service returns any cards. */ public void startQuickAccessUiIntent(ActivityStarter activityStarter, - ActivityLaunchAnimator.Controller animationController, + ActivityTransitionAnimator.Controller animationController, boolean hasCard) { mQuickAccessWalletClient.getWalletPendingIntent(mExecutor, walletPendingIntent -> { @@ -271,7 +271,7 @@ public class QuickAccessWalletController { private void startQuickAccessViaIntent(Intent intent, boolean hasCard, ActivityStarter activityStarter, - ActivityLaunchAnimator.Controller animationController) { + ActivityTransitionAnimator.Controller animationController) { if (hasCard) { activityStarter.startActivity(intent, true /* dismissShade */, animationController, true /* showOverLockscreenWhenLocked */); @@ -285,7 +285,7 @@ public class QuickAccessWalletController { private void startQuickAccessViaPendingIntent(PendingIntent pendingIntent, ActivityStarter activityStarter, - ActivityLaunchAnimator.Controller animationController) { + ActivityTransitionAnimator.Controller animationController) { activityStarter.postStartActivityDismissingKeyguard( pendingIntent, animationController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 722107c7a1b5..75a49d73cb59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -44,37 +44,37 @@ import org.mockito.junit.MockitoJUnit @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper -class ActivityLaunchAnimatorTest : SysuiTestCase() { +class ActivityTransitionAnimatorTest : SysuiTestCase() { private val transitionContainer = LinearLayout(mContext) private val testTransitionAnimator = fakeTransitionAnimator() - @Mock lateinit var callback: ActivityLaunchAnimator.Callback - @Mock lateinit var listener: ActivityLaunchAnimator.Listener - @Spy private val controller = TestLaunchAnimatorController(transitionContainer) + @Mock lateinit var callback: ActivityTransitionAnimator.Callback + @Mock lateinit var listener: ActivityTransitionAnimator.Listener + @Spy private val controller = TestTransitionAnimatorController(transitionContainer) @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback - private lateinit var activityLaunchAnimator: ActivityLaunchAnimator + private lateinit var activityTransitionAnimator: ActivityTransitionAnimator @get:Rule val rule = MockitoJUnit.rule() @Before fun setup() { - activityLaunchAnimator = - ActivityLaunchAnimator( + activityTransitionAnimator = + ActivityTransitionAnimator( testTransitionAnimator, testTransitionAnimator, disableWmTimeout = true ) - activityLaunchAnimator.callback = callback - activityLaunchAnimator.addListener(listener) + activityTransitionAnimator.callback = callback + activityTransitionAnimator.addListener(listener) } @After fun tearDown() { - activityLaunchAnimator.removeListener(listener) + activityTransitionAnimator.removeListener(listener) } private fun startIntentWithAnimation( - animator: ActivityLaunchAnimator = this.activityLaunchAnimator, - controller: ActivityLaunchAnimator.Controller? = this.controller, + animator: ActivityTransitionAnimator = this.activityTransitionAnimator, + controller: ActivityTransitionAnimator.Controller? = this.controller, animate: Boolean = true, intentStarter: (RemoteAnimationAdapter?) -> Int ) { @@ -138,7 +138,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java) var animationAdapter: RemoteAnimationAdapter? = null - startIntentWithAnimation(activityLaunchAnimator) { adapter -> + startIntentWithAnimation(activityTransitionAnimator) { adapter -> animationAdapter = adapter ActivityManager.START_DELIVERED_TO_TOP } @@ -163,50 +163,50 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { @Test fun doesNotStartIfAnimationIsCancelled() { - val runner = activityLaunchAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createRunner(controller) runner.onAnimationCancelled() runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback) waitForIdleSync() - verify(controller).onLaunchAnimationCancelled() + verify(controller).onTransitionAnimationCancelled() verify(controller, never()).onTransitionAnimationStart(anyBoolean()) - verify(listener).onLaunchAnimationCancelled() - verify(listener, never()).onLaunchAnimationStart() + verify(listener).onTransitionAnimationCancelled() + verify(listener, never()).onTransitionAnimationStart() assertNull(runner.delegate) } @Test fun cancelsIfNoOpeningWindowIsFound() { - val runner = activityLaunchAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createRunner(controller) runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback) waitForIdleSync() - verify(controller).onLaunchAnimationCancelled() + verify(controller).onTransitionAnimationCancelled() verify(controller, never()).onTransitionAnimationStart(anyBoolean()) - verify(listener).onLaunchAnimationCancelled() - verify(listener, never()).onLaunchAnimationStart() + verify(listener).onTransitionAnimationCancelled() + verify(listener, never()).onTransitionAnimationStart() assertNull(runner.delegate) } @Test fun startsAnimationIfWindowIsOpening() { - val runner = activityLaunchAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createRunner(controller) runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback) waitForIdleSync() - verify(listener).onLaunchAnimationStart() + verify(listener).onTransitionAnimationStart() verify(controller).onTransitionAnimationStart(anyBoolean()) } @Test fun creatingControllerFromNormalViewThrows() { assertThrows(IllegalArgumentException::class.java) { - ActivityLaunchAnimator.Controller.fromView(FrameLayout(mContext)) + ActivityTransitionAnimator.Controller.fromView(FrameLayout(mContext)) } } @Test fun disposeRunner_delegateDereferenced() { - val runner = activityLaunchAnimator.createRunner(controller) + val runner = activityTransitionAnimator.createRunner(controller) assertNotNull(runner.delegate) runner.dispose() waitForIdleSync() @@ -241,11 +241,11 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { } /** - * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called + * A simple implementation of [ActivityTransitionAnimator.Controller] which throws if it is called * outside of the main thread. */ -private class TestLaunchAnimatorController(override var transitionContainer: ViewGroup) : - ActivityLaunchAnimator.Controller { +private class TestTransitionAnimatorController(override var transitionContainer: ViewGroup) : + ActivityTransitionAnimator.Controller { override fun createAnimatorState() = TransitionAnimator.State( top = 100, @@ -282,7 +282,7 @@ private class TestLaunchAnimatorController(override var transitionContainer: Vie assertOnMainThread() } - override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { + override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) { assertOnMainThread() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt index 8442a62d7c40..b31fe21f8e91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt @@ -29,12 +29,12 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper -class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() { +class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() { @Test fun animatingOrphanViewDoesNotCrash() { val state = TransitionAnimator.State(top = 0, bottom = 0, left = 0, right = 0) - val controller = GhostedViewLaunchAnimatorController(LaunchableFrameLayout(mContext)) + val controller = GhostedViewTransitionAnimatorController(LaunchableFrameLayout(mContext)) controller.onIntentStarted(willAnimate = true) controller.onTransitionAnimationStart(isExpandingFullyAbove = true) controller.onTransitionAnimationProgress(state, progress = 0f, linearProgress = 0f) @@ -44,7 +44,7 @@ class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() { @Test fun creatingControllerFromNormalViewThrows() { assertThrows(IllegalArgumentException::class.java) { - GhostedViewLaunchAnimatorController(FrameLayout(mContext)) + GhostedViewTransitionAnimatorController(FrameLayout(mContext)) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java index 21397d97b578..1c6f25147a1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java @@ -153,7 +153,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testReportedDisplayBounds() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -175,7 +175,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testEntryTouchZone() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Rect touchArea = new Rect(4, 4, 8 , 8); doAnswer(invocation -> { @@ -203,10 +203,10 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testSessionCount() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Rect touchArea = new Rect(4, 4, 8 , 8); - final DreamTouchHandler unzonedTouchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler unzonedTouchHandler = createTouchHandler(); doAnswer(invocation -> { final Region region = (Region) invocation.getArguments()[1]; region.set(touchArea); @@ -248,9 +248,28 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { } } + @Test - public void testInputEventPropagation() { + public void testNoActiveSessionWhenHandlerDisabled() { final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + // disable the handler + when(touchHandler.isEnabled()).thenReturn(false); + + final Environment environment = new Environment(Stream.of(touchHandler) + .collect(Collectors.toCollection(HashSet::new))); + final MotionEvent initialEvent = Mockito.mock(MotionEvent.class); + when(initialEvent.getX()).thenReturn(5.0f); + when(initialEvent.getY()).thenReturn(5.0f); + environment.publishInputEvent(initialEvent); + + // Make sure there is no active session. + verify(touchHandler, never()).onSessionStart(any()); + verify(touchHandler, never()).getTouchInitiationRegion(any(), any()); + } + + @Test + public void testInputEventPropagation() { + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -270,7 +289,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testInputEventPropagationAfterRemoval() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -294,7 +313,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testInputGesturePropagation() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -313,7 +332,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testGestureConsumption() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -336,8 +355,9 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testBroadcast() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); - final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); + final DreamTouchHandler touchHandler2 = createTouchHandler(); + when(touchHandler2.isEnabled()).thenReturn(true); final Environment environment = new Environment(Stream.of(touchHandler, touchHandler2) .collect(Collectors.toCollection(HashSet::new))); @@ -361,7 +381,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testPush() throws InterruptedException, ExecutionException { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -404,7 +424,8 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testPop() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); + final DreamTouchHandler.TouchSession.Callback callback = Mockito.mock(DreamTouchHandler.TouchSession.Callback.class); @@ -424,7 +445,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testPauseWithNoActiveSessions() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -438,7 +459,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testDeferredPauseWithActiveSessions() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -476,7 +497,7 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testDestroyWithActiveSessions() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler) .collect(Collectors.toCollection(HashSet::new))); @@ -509,9 +530,8 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testPilfering() { - final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class); - final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class); - + final DreamTouchHandler touchHandler1 = createTouchHandler(); + final DreamTouchHandler touchHandler2 = createTouchHandler(); final Environment environment = new Environment(Stream.of(touchHandler1, touchHandler2) .collect(Collectors.toCollection(HashSet::new))); @@ -543,7 +563,8 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { @Test public void testOnRemovedCallbackOnStopMonitoring() { - final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + final DreamTouchHandler touchHandler = createTouchHandler(); + final DreamTouchHandler.TouchSession.Callback callback = Mockito.mock(DreamTouchHandler.TouchSession.Callback.class); @@ -607,4 +628,11 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase { DreamTouchHandler handler) { return registerInputEventListener(captureSession(handler)); } + + private DreamTouchHandler createTouchHandler() { + final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class); + // enable the handler by default + when(touchHandler.isEnabled()).thenReturn(true); + return touchHandler; + } } 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 24cf16479188..2732047b4eba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -86,7 +86,7 @@ import com.android.keyguard.TestScopeProvider; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; @@ -186,7 +186,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeController mShadeController; private NotificationShadeWindowController mNotificationShadeWindowController; private @Mock DreamOverlayStateController mDreamOverlayStateController; - private @Mock ActivityLaunchAnimator mActivityLaunchAnimator; + private @Mock ActivityTransitionAnimator mActivityTransitionAnimator; private @Mock ScrimController mScrimController; private @Mock IActivityTaskManager mActivityTaskManagerService; private @Mock SysuiColorExtractor mColorExtractor; @@ -763,7 +763,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @Test public void testUpdateIsKeyguardAfterOccludeAnimationIsCancelled() { - mViewMediator.mOccludeAnimationController.onLaunchAnimationCancelled( + mViewMediator.mOccludeAnimationController.onTransitionAnimationCancelled( null /* newKeyguardOccludedState */); // Since the updateIsKeyguard call is delayed during the animation, ensure it's called if @@ -1231,7 +1231,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mWallpaperRepository, () -> mShadeController, () -> mNotificationShadeWindowController, - () -> mActivityLaunchAnimator, + () -> mActivityTransitionAnimator, () -> mScrimController, mActivityTaskManagerService, mFeatureFlags, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index b4ae7e3a7ca9..798c7f757b58 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -24,7 +24,7 @@ import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription @@ -225,7 +225,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller + @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller @Mock private lateinit var expandable: Expandable @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var devicePolicyManager: DevicePolicyManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS deleted file mode 100644 index 142862d7e480..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Haptics team also works on Ringtones (RingtonePlayer/NotificationPlayer) -file:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index c6cfabc61300..32b6f3854bfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -72,7 +72,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.nearby.NearbyMediaDevicesManager; @@ -110,7 +110,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { @Mock private DialogLaunchAnimator mDialogLaunchAnimator; @Mock - private ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController; + private ActivityTransitionAnimator.Controller mActivityTransitionAnimatorController; @Mock private NearbyMediaDevicesManager mNearbyMediaDevicesManager; // Mock @@ -143,7 +143,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { @Mock private KeyguardManager mKeyguardManager; @Mock - private ActivityLaunchAnimator.Controller mController; + private ActivityTransitionAnimator.Controller mController; @Mock private PowerExemptionManager mPowerExemptionManager; @Mock @@ -1122,7 +1122,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { @Test public void launchBluetoothPairing_isKeyguardLocked_dismissDialog() { when(mDialogLaunchAnimator.createActivityLaunchController(mDialogLaunchView)).thenReturn( - mActivityLaunchAnimatorController); + mActivityTransitionAnimatorController); when(mKeyguardManager.isKeyguardLocked()).thenReturn(true); mMediaOutputController.mCallback = this.mCallback; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index ae47a7bfa63d..33f8f1fd9087 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -38,7 +38,7 @@ import android.view.IWindowManager import android.view.View import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.view.LaunchableFrameLayout import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter @@ -372,7 +372,7 @@ class CustomTileTest : SysuiTestCase() { verify(activityStarter) .startPendingIntentMaybeDismissingKeyguard( - eq(pi), nullable(), nullable<ActivityLaunchAnimator.Controller>()) + eq(pi), nullable(), nullable<ActivityTransitionAnimator.Controller>()) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt index 23466cc20f44..720c25a3f719 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt @@ -26,7 +26,7 @@ import com.android.internal.logging.nano.MetricsProto import com.android.internal.logging.testing.FakeMetricsLogger import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.globalactions.GlobalActionsDialogLite import com.android.systemui.plugins.ActivityStarter @@ -127,7 +127,7 @@ class FooterActionsInteractorTest : SysuiTestCase() { .startActivity( intentCaptor.capture(), /* dismissShade= */ eq(true), - nullable() as? ActivityLaunchAnimator.Controller, + nullable() as? ActivityTransitionAnimator.Controller, ) assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SETTINGS) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index 3bf59ca62024..874368bd2bd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -29,7 +29,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.systemui.res.R import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlInfo @@ -348,7 +348,7 @@ class DeviceControlsTileTest : SysuiTestCase() { verify(activityStarter).startActivity( intentCaptor.capture(), eq(true) /* dismissShade */, - nullable(ActivityLaunchAnimator.Controller::class.java), + nullable(ActivityTransitionAnimator.Controller::class.java), eq(true) /* showOverLockscreenWhenLocked */) assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME) } @@ -379,7 +379,7 @@ class DeviceControlsTileTest : SysuiTestCase() { verify(activityStarter).startActivity( intentCaptor.capture(), anyBoolean() /* dismissShade */, - nullable(ActivityLaunchAnimator.Controller::class.java), + nullable(ActivityTransitionAnimator.Controller::class.java), eq(false) /* showOverLockscreenWhenLocked */) assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 9517f823cf10..1dc5f7dbf6fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -103,8 +103,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { ) testableLooper = TestableLooper.get(this) - communalRepository.setIsCommunalEnabled(true) - whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any())) .thenReturn(bouncerShowingFlow) whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow) @@ -125,36 +123,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun isEnabled_communalEnabled_returnsTrue() { - communalRepository.setIsCommunalEnabled(true) - - assertThat(underTest.isEnabled()).isTrue() - } - - @Test - fun isEnabled_communalDisabled_returnsFalse() { - communalRepository.setIsCommunalEnabled(false) - - assertThat(underTest.isEnabled()).isFalse() - } - - @Test - fun initView_notEnabled_throwsException() { - communalRepository.setIsCommunalEnabled(false) - - underTest = - GlanceableHubContainerController( - communalInteractor, - communalViewModel, - keyguardTransitionInteractor, - shadeInteractor, - powerManager, - ) - - assertThrows(RuntimeException::class.java) { underTest.initView(context) } - } - - @Test fun initView_calledTwice_throwsException() { underTest = GlanceableHubContainerController( 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 22b05be507c6..248ed249c213 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -488,7 +488,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { return } - whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true) + whenever(mGlanceableHubContainerController.communalAvailable()) + .thenReturn(MutableStateFlow(true)) val mockCommunalView = mock(View::class.java) whenever(mGlanceableHubContainerController.initView(any<Context>())) @@ -513,7 +514,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { return } - whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false) whenever(mGlanceableHubContainerController.communalAvailable()) .thenReturn(MutableStateFlow(false)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt index f58ff0adb2b2..6f16d65e14f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt @@ -36,7 +36,7 @@ import org.mockito.junit.MockitoJUnit @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper -class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { +class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { @Mock lateinit var notificationListContainer: NotificationListContainer @Mock lateinit var headsUpManager: HeadsUpManager @Mock lateinit var jankMonitor: InteractionJankMonitor @@ -44,7 +44,7 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { private lateinit var notificationTestHelper: NotificationTestHelper private lateinit var notification: ExpandableNotificationRow - private lateinit var controller: NotificationLaunchAnimatorController + private lateinit var controller: NotificationTransitionAnimatorController private val notificationLaunchAnimationInteractor = NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()) @@ -62,7 +62,7 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) notification = notificationTestHelper.createRow() controller = - NotificationLaunchAnimatorController( + NotificationTransitionAnimatorController( notificationLaunchAnimationInteractor, notificationListContainer, headsUpManager, @@ -97,7 +97,7 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { @Test fun testHunIsRemovedAndCallbackIsInvokedWhenAnimationIsCancelled() { flagNotificationAsHun() - controller.onLaunchAnimationCancelled() + controller.onTransitionAnimationCancelled() assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification)) assertFalse(notification.entry.isExpandAnimationRunning) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index a1daff14eea6..b4dadaf83e94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -102,6 +102,7 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) fun testSetNotificationStats_clearableAlerting() { whenever(section.bucket).thenReturn(BUCKET_ALERTING) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) @@ -109,6 +110,7 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) fun testSetNotificationStats_clearableSilent() { whenever(section.bucket).thenReturn(BUCKET_SILENT) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt deleted file mode 100644 index 9b641f014c01..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt +++ /dev/null @@ -1,153 +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.statusbar.notification.domain.interactor - -import androidx.test.filters.SmallTest -import com.android.systemui.SysUITestComponent -import com.android.systemui.SysUITestModule -import com.android.systemui.SysuiTestCase -import com.android.systemui.collectLastValue -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.runCurrent -import com.android.systemui.runTest -import com.android.systemui.statusbar.notification.collection.render.NotifStats -import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository -import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs -import com.google.common.truth.Truth.assertThat -import dagger.BindsInstance -import dagger.Component -import org.junit.Test - -@SmallTest -class ActiveNotificationsInteractorTest : SysuiTestCase() { - - @Component(modules = [SysUITestModule::class]) - @SysUISingleton - interface TestComponent : SysUITestComponent<ActiveNotificationsInteractor> { - val activeNotificationListRepository: ActiveNotificationListRepository - - @Component.Factory - interface Factory { - fun create(@BindsInstance test: SysuiTestCase): TestComponent - } - } - - private val testComponent: TestComponent = - DaggerActiveNotificationsInteractorTest_TestComponent.factory().create(test = this) - - @Test - fun testAllNotificationsCount() = - testComponent.runTest { - val count by collectLastValue(underTest.allNotificationsCount) - - activeNotificationListRepository.setActiveNotifs(5) - runCurrent() - - assertThat(count).isEqualTo(5) - assertThat(underTest.allNotificationsCountValue).isEqualTo(5) - } - - @Test - fun testAreAnyNotificationsPresent_isTrue() = - testComponent.runTest { - val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent) - - activeNotificationListRepository.setActiveNotifs(2) - runCurrent() - - assertThat(areAnyNotificationsPresent).isTrue() - assertThat(underTest.areAnyNotificationsPresentValue).isTrue() - } - - @Test - fun testAreAnyNotificationsPresent_isFalse() = - testComponent.runTest { - val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent) - - activeNotificationListRepository.setActiveNotifs(0) - runCurrent() - - assertThat(areAnyNotificationsPresent).isFalse() - assertThat(underTest.areAnyNotificationsPresentValue).isFalse() - } - - @Test - fun testActiveNotificationRanks_sizeMatches() { - testComponent.runTest { - val activeNotificationRanks by collectLastValue(underTest.activeNotificationRanks) - - activeNotificationListRepository.setActiveNotifs(5) - runCurrent() - - assertThat(activeNotificationRanks!!.size).isEqualTo(5) - } - } - - @Test - fun testHasClearableNotifications_whenHasClearableAlertingNotifs() = - testComponent.runTest { - val hasClearable by collectLastValue(underTest.hasClearableNotifications) - - activeNotificationListRepository.notifStats.value = - NotifStats( - numActiveNotifs = 2, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = true, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = false, - ) - runCurrent() - - assertThat(hasClearable).isTrue() - } - - @Test - fun testHasClearableNotifications_whenHasClearableSilentNotifs() = - testComponent.runTest { - val hasClearable by collectLastValue(underTest.hasClearableNotifications) - - activeNotificationListRepository.notifStats.value = - NotifStats( - numActiveNotifs = 2, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = false, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = true, - ) - runCurrent() - - assertThat(hasClearable).isTrue() - } - - @Test - fun testHasClearableNotifications_whenHasNoClearableNotifs() = - testComponent.runTest { - val hasClearable by collectLastValue(underTest.hasClearableNotifications) - - activeNotificationListRepository.notifStats.value = - NotifStats( - numActiveNotifs = 2, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = false, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = false, - ) - runCurrent() - - assertThat(hasClearable).isFalse() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index b922ab39912b..3811f04a365d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -472,6 +472,23 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { } @Test + public void publicMode_nullChannel_allowed() { + mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(mEntry); + + // WHEN the notification's user is in public mode and settings are configured to disallow + // notifications in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true); + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setVisibilityOverride(VISIBILITY_SECRET).build()); + + // THEN allow the entry + assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test public void publicMode_notifDisallowed() { mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 04f3216c8e73..f326ceaf1950 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -614,7 +614,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { selected[0] = selectedRows; }); - mStackScroller.clearNotifications(ROWS_ALL, true); + mStackScroller.clearNotifications(ROWS_ALL, + /* closeShade = */ true, + /* hideSilentSection = */ true); assertEquals(1, numCalls[0]); assertEquals(ROWS_ALL, selected[0]); } @@ -628,7 +630,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { selected[0] = selectedRows; }); - mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_GENTLE, false); + mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_GENTLE, + /* closeShade = */ false, + /* hideSilentSection = */ true); assertEquals(1, numCalls[0]); assertEquals(ROWS_GENTLE, selected[0]); } @@ -640,7 +644,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { doReturn(true).when(mStackScroller).isVisible(row); mStackScroller.addContainerView(row); - mStackScroller.clearNotifications(ROWS_ALL, false); + mStackScroller.clearNotifications(ROWS_ALL, + /* closeShade = */ false, + /* hideSilentSection = */ false); assertClearAllInProgress(true); verify(mNotificationRoundnessManager).setClearAllInProgress(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt index 5a5703512a39..dfbe1acd1755 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.stack import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent @@ -28,6 +30,7 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIM import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -38,9 +41,12 @@ import org.mockito.Mockito.eq import org.mockito.Mockito.verify private const val VIEW_HEIGHT = 100 +private const val FULL_SHADE_APPEAR_TRANSLATION = 300 +private const val HEADS_UP_ABOVE_SCREEN = 80 @SmallTest @RunWith(AndroidTestingRunner::class) +@RunWithLooper class StackStateAnimatorTest : SysuiTestCase() { private lateinit var stackStateAnimator: StackStateAnimator @@ -51,9 +57,15 @@ class StackStateAnimatorTest : SysuiTestCase() { private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor() @Before fun setUp() { + overrideResource( + R.dimen.go_to_full_shade_appearing_translation, + FULL_SHADE_APPEAR_TRANSLATION + ) + overrideResource(R.dimen.heads_up_appear_y_above_screen, HEADS_UP_ABOVE_SCREEN) + whenever(stackScroller.context).thenReturn(context) whenever(view.viewState).thenReturn(viewState) - stackStateAnimator = StackStateAnimator(stackScroller) + stackStateAnimator = StackStateAnimator(mContext, stackScroller) } @Test @@ -122,4 +134,22 @@ class StackStateAnimatorTest : SysuiTestCase() { verify(view, description("should be called at the end of the animation")) .removeFromTransientContainer() } + + @Test + fun initView_updatesResources() { + // Given: the resource values are initialized in the SSA + assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation) + .isEqualTo(FULL_SHADE_APPEAR_TRANSLATION) + assertThat(stackStateAnimator.mHeadsUpAppearStartAboveScreen) + .isEqualTo(HEADS_UP_ABOVE_SCREEN) + + // When: initView is called after the resources have changed + overrideResource(R.dimen.go_to_full_shade_appearing_translation, 200) + overrideResource(R.dimen.heads_up_appear_y_above_screen, 100) + stackStateAnimator.initView(mContext) + + // Then: the resource values are updated + assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation).isEqualTo(200) + assertThat(stackStateAnimator.mHeadsUpAppearStartAboveScreen).isEqualTo(100) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index 3a7659dd00e4..0a18eb66c4df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -28,11 +28,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository -import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState -import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakefulnessState @@ -70,7 +66,6 @@ class NotificationListViewModelTest : SysuiTestCase() { private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val fakeConfigurationController = kosmos.fakeConfigurationController private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository - private val fakeKeyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val fakePowerRepository = kosmos.fakePowerRepository private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository private val fakeShadeRepository = kosmos.fakeShadeRepository @@ -90,11 +85,7 @@ class NotificationListViewModelTest : SysuiTestCase() { val important by collectLastValue(underTest.isImportantForAccessibility) // WHEN on lockscreen - fakeKeyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - testScope, - ) + fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) // AND has no notifs activeNotificationListRepository.setActiveNotifs(count = 0) testScope.runCurrent() @@ -109,11 +100,7 @@ class NotificationListViewModelTest : SysuiTestCase() { val important by collectLastValue(underTest.isImportantForAccessibility) // WHEN on lockscreen - fakeKeyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - testScope, - ) + fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) // AND has notifs activeNotificationListRepository.setActiveNotifs(count = 2) runCurrent() @@ -128,11 +115,7 @@ class NotificationListViewModelTest : SysuiTestCase() { val important by collectLastValue(underTest.isImportantForAccessibility) // WHEN not on lockscreen - fakeKeyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - testScope, - ) + fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) // AND has no notifs activeNotificationListRepository.setActiveNotifs(count = 0) runCurrent() @@ -150,7 +133,7 @@ class NotificationListViewModelTest : SysuiTestCase() { activeNotificationListRepository.setActiveNotifs(count = 0) runCurrent() - // THEN should show + // THEN empty shade is visible assertThat(shouldShow).isTrue() } @@ -163,7 +146,7 @@ class NotificationListViewModelTest : SysuiTestCase() { activeNotificationListRepository.setActiveNotifs(count = 2) runCurrent() - // THEN should not show + // THEN empty shade is not visible assertThat(shouldShow).isFalse() } @@ -178,7 +161,7 @@ class NotificationListViewModelTest : SysuiTestCase() { fakeShadeRepository.legacyQsFullscreen.value = true runCurrent() - // THEN should not show + // THEN empty shade is not visible assertThat(shouldShow).isFalse() } @@ -196,48 +179,54 @@ class NotificationListViewModelTest : SysuiTestCase() { fakeConfigurationController.notifyConfigurationChanged() runCurrent() - // THEN should show + // THEN empty shade is visible + assertThat(shouldShow).isTrue() + } + + @Test + fun testShouldShowEmptyShadeView_trueWhenLockedShade() = + testScope.runTest { + val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView) + + // WHEN has no notifs + activeNotificationListRepository.setActiveNotifs(count = 0) + // AND shade is open + fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) + runCurrent() + + // THEN empty shade is visible assertThat(shouldShow).isTrue() } @Test - fun testShouldShowEmptyShadeView_falseWhenTransitioningToAOD() = + fun testShouldShowEmptyShadeView_falseWhenKeyguard() = testScope.runTest { val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView) // WHEN has no notifs activeNotificationListRepository.setActiveNotifs(count = 0) - // AND transitioning to AOD - fakeKeyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 0f, - ) - ) + // AND shade is open + fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) runCurrent() - // THEN should not show + // THEN empty shade is not visible assertThat(shouldShow).isFalse() } @Test - fun testShouldShowEmptyShadeView_falseWhenBouncerShowing() = + fun testShouldShowEmptyShadeView_falseWhenStartingToSleep() = testScope.runTest { val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView) // WHEN has no notifs activeNotificationListRepository.setActiveNotifs(count = 0) - // AND is on bouncer - fakeKeyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - testScope, - ) + // AND shade is open + fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) + // AND device is starting to go to sleep + fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP) runCurrent() - // THEN should not show + // THEN empty shade is not visible assertThat(shouldShow).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index b048949e0e76..3a94295de668 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -83,7 +83,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.back.domain.interactor.BackActionInteractor; import com.android.systemui.biometrics.AuthRippleController; @@ -250,7 +250,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private StatusBarStateControllerImpl mStatusBarStateController; @Mock private BatteryController mBatteryController; @Mock private DeviceProvisionedController mDeviceProvisionedController; - @Mock private NotificationLaunchAnimatorControllerProvider mNotifLaunchAnimControllerProvider; + @Mock private NotificationLaunchAnimatorControllerProvider + mNotifTransitionAnimControllerProvider; @Mock private StatusBarNotificationPresenter mNotificationPresenter; @Mock private NotificationActivityStarter mNotificationActivityStarter; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; @@ -307,7 +308,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private StartingSurface mStartingSurface; @Mock private OperatorNameViewController mOperatorNameViewController; @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory; - @Mock private ActivityLaunchAnimator mActivityLaunchAnimator; + @Mock private ActivityTransitionAnimator mActivityTransitionAnimator; @Mock private DeviceStateManager mDeviceStateManager; @Mock private WiredChargingRippleController mWiredChargingRippleController; @Mock private Lazy<CameraLauncher> mCameraLauncherLazy; @@ -504,7 +505,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mStackScrollerController, (Lazy<NotificationPresenter>) () -> mNotificationPresenter, (Lazy<NotificationActivityStarter>) () -> mNotificationActivityStarter, - mNotifLaunchAnimControllerProvider, + mNotifTransitionAnimControllerProvider, mDozeParameters, mScrimController, mBiometricUnlockControllerLazy, @@ -543,7 +544,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new MessageRouterImpl(mMainExecutor), mWallpaperManager, Optional.of(mStartingSurface), - mActivityLaunchAnimator, + mActivityTransitionAnimator, mDeviceStateManager, mWiredChargingRippleController, mDreamManager, @@ -1053,6 +1054,24 @@ public class CentralSurfacesImplTest extends SysuiTestCase { verify(mBubbles).onStatusBarVisibilityChanged(true); } + @Test + public void updateResources_flagEnabled_doesNotUpdateStatusBarWindowHeight() { + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX); + + mCentralSurfaces.updateResources(); + + verify(mStatusBarWindowController, never()).refreshStatusBarHeight(); + } + + @Test + public void updateResources_flagDisabled_updatesStatusBarWindowHeight() { + mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX); + + mCentralSurfaces.updateResources(); + + verify(mStatusBarWindowController).refreshStatusBarHeight(); + } + /** * Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard} * to reconfigure the keyguard to reflect the requested showing/occluded states. 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 65d71f8b4540..6eb1c1a9f12c 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 @@ -29,16 +29,20 @@ import android.view.RoundedCorners import android.view.WindowInsets import android.widget.FrameLayout import androidx.test.filters.SmallTest +import com.android.systemui.Flags import com.android.systemui.Gefingerpoken import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R +import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test +import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.verify @SmallTest @RunWithLooper(setAsMainLooper = true) @@ -47,6 +51,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() { private lateinit var view: PhoneStatusBarView private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>() + private val windowController = mock<StatusBarWindowController>() @Before fun setUp() { @@ -55,8 +60,11 @@ class PhoneStatusBarViewTest : SysuiTestCase() { contentInsetsProvider ) mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>()) + mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController) view = spy(createStatusBarView()) whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets()) + whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(Insets.NONE) } @Test @@ -110,6 +118,42 @@ class PhoneStatusBarViewTest : SysuiTestCase() { } @Test + fun onAttachedToWindow_flagEnabled_updatesWindowHeight() { + mSetFlagsRule.enableFlags(Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX) + + view.onAttachedToWindow() + + verify(windowController).refreshStatusBarHeight() + } + + @Test + fun onAttachedToWindow_flagDisabled_doesNotUpdateWindowHeight() { + mSetFlagsRule.disableFlags(Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX) + + view.onAttachedToWindow() + + verify(windowController, never()).refreshStatusBarHeight() + } + + @Test + fun onConfigurationChanged_flagEnabled_updatesWindowHeight() { + mSetFlagsRule.enableFlags(Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX) + + view.onConfigurationChanged(Configuration()) + + verify(windowController).refreshStatusBarHeight() + } + + @Test + fun onConfigurationChanged_flagDisabled_doesNotUpdateWindowHeight() { + mSetFlagsRule.disableFlags(Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX) + + view.onConfigurationChanged(Configuration()) + + verify(windowController, never()).refreshStatusBarHeight() + } + + @Test fun onAttachedToWindow_updatesLeftTopRightPaddingsBasedOnInsets() { val insets = Insets.of(/* left = */ 10, /* top = */ 20, /* right = */ 30, /* bottom = */ 40) whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 597e2e34f1d4..41514ce3e72c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -63,7 +63,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.flags.FakeFeatureFlags; @@ -159,7 +159,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private StatusBarNotificationActivityStarter mNotificationActivityStarter; @Mock - private ActivityLaunchAnimator mActivityLaunchAnimator; + private ActivityTransitionAnimator mActivityTransitionAnimator; @Mock private InteractionJankMonitor mJankMonitor; private FakePowerRepository mPowerRepository; @@ -255,7 +255,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(NotificationPresenter.class), mock(ShadeViewController.class), mock(NotificationShadeWindowController.class), - mActivityLaunchAnimator, + mActivityTransitionAnimator, new ShadeAnimationInteractorLegacyImpl( new ShadeAnimationRepository(), new FakeShadeRepository()), notificationAnimationProvider, @@ -306,7 +306,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Then verify(mShadeController, atLeastOnce()).collapseShade(); - verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(), + verify(mActivityTransitionAnimator).startPendingIntentWithAnimation(any(), eq(false) /* animate */, any(), any()); verify(mAssistManager).hideAssist(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt index 614261d476c6..203096affd5c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseContro import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController.Companion.AnimationState.EASE_OUT import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController.Companion.AnimationState.MAIN import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController.Companion.AnimationState.NOT_PLAYING +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -47,7 +48,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { assertThat(turbulenceNoiseController.state).isEqualTo(NOT_PLAYING) fakeExecutor.execute { - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) assertThat(turbulenceNoiseController.state).isEqualTo(EASE_IN) @@ -75,7 +76,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { fakeExecutor.execute { // Request another animation - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) assertThat(turbulenceNoiseController.state).isEqualTo(MAIN) } @@ -89,7 +90,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { TurbulenceNoiseController(turbulenceNoiseView).also { it.state = MAIN } fakeExecutor.execute { - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) fakeSystemClock.advanceTime(config.maxDuration.toLong() / 2) @@ -107,7 +108,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { TurbulenceNoiseController(turbulenceNoiseView).also { it.state = EASE_IN } fakeExecutor.execute { - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) fakeSystemClock.advanceTime(config.maxDuration.toLong() / 2) @@ -128,7 +129,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { assertThat(turbulenceNoiseView.noiseConfig).isNull() fakeExecutor.execute { - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) assertThat(turbulenceNoiseController.state).isEqualTo(EASE_IN) assertThat(turbulenceNoiseView.visibility).isEqualTo(VISIBLE) @@ -156,7 +157,7 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() { val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView) fakeExecutor.execute { - turbulenceNoiseController.play(config) + turbulenceNoiseController.play(SIMPLEX_NOISE, config) turbulenceNoiseController.updateNoiseColor(expectedColor) diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt index 71bd51174452..549280a809e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt @@ -18,6 +18,8 @@ package com.android.systemui.surfaceeffects.turbulencenoise import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE_FRACTAL import org.junit.Test import org.junit.runner.RunWith @@ -28,12 +30,12 @@ class TurbulenceNoiseShaderTest : SysuiTestCase() { private lateinit var turbulenceNoiseShader: TurbulenceNoiseShader @Test - fun compliesSimplexNoise() { - turbulenceNoiseShader = TurbulenceNoiseShader() + fun compilesSimplexNoise() { + turbulenceNoiseShader = TurbulenceNoiseShader(baseType = SIMPLEX_NOISE) } @Test - fun compliesFractalNoise() { - turbulenceNoiseShader = TurbulenceNoiseShader(useFractal = true) + fun compilesFractalNoise() { + turbulenceNoiseShader = TurbulenceNoiseShader(baseType = SIMPLEX_NOISE_FRACTAL) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt index ce7f2f4eb6ea..953071cb0a02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt @@ -15,9 +15,12 @@ */ package com.android.systemui.surfaceeffects.turbulencenoise +import android.graphics.Color import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE_FRACTAL import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -35,7 +38,8 @@ class TurbulenceNoiseViewTest : SysuiTestCase() { @Test fun play_playsAnimation() { val config = TurbulenceNoiseAnimationConfig() - val turbulenceNoiseView = TurbulenceNoiseView(context, null).also { it.applyConfig(config) } + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(SIMPLEX_NOISE, config) var onAnimationEndCalled = false fakeExecutor.execute { @@ -50,7 +54,8 @@ class TurbulenceNoiseViewTest : SysuiTestCase() { @Test fun playEaseIn_playsEaseInAnimation() { val config = TurbulenceNoiseAnimationConfig() - val turbulenceNoiseView = TurbulenceNoiseView(context, null).also { it.applyConfig(config) } + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(SIMPLEX_NOISE, config) var onAnimationEndCalled = false fakeExecutor.execute { @@ -65,7 +70,8 @@ class TurbulenceNoiseViewTest : SysuiTestCase() { @Test fun playEaseOut_playsEaseOutAnimation() { val config = TurbulenceNoiseAnimationConfig() - val turbulenceNoiseView = TurbulenceNoiseView(context, null).also { it.applyConfig(config) } + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(SIMPLEX_NOISE, config) var onAnimationEndCalled = false fakeExecutor.execute { @@ -80,7 +86,8 @@ class TurbulenceNoiseViewTest : SysuiTestCase() { @Test fun finish_animationPlaying_finishesAnimation() { val config = TurbulenceNoiseAnimationConfig() - val turbulenceNoiseView = TurbulenceNoiseView(context, null).also { it.applyConfig(config) } + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(SIMPLEX_NOISE, config) var onAnimationEndCalled = false fakeExecutor.execute { @@ -94,4 +101,51 @@ class TurbulenceNoiseViewTest : SysuiTestCase() { assertThat(turbulenceNoiseView.currentAnimator).isNull() } } + + @Test + fun initShader_createsShaderCorrectly() { + val config = TurbulenceNoiseAnimationConfig() + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + + // To begin with, the shader is not initialized yet. + assertThat(turbulenceNoiseView.turbulenceNoiseShader).isNull() + + turbulenceNoiseView.initShader(baseType = SIMPLEX_NOISE, config) + + assertThat(turbulenceNoiseView.turbulenceNoiseShader).isNotNull() + assertThat(turbulenceNoiseView.turbulenceNoiseShader!!.baseType).isEqualTo(SIMPLEX_NOISE) + } + + @Test + fun initShader_changesConfig_doesNotCreateNewShader() { + val config = TurbulenceNoiseAnimationConfig(color = Color.RED) + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(baseType = SIMPLEX_NOISE, config) + + val shader = turbulenceNoiseView.turbulenceNoiseShader + assertThat(shader).isNotNull() + + val newConfig = TurbulenceNoiseAnimationConfig(color = Color.GREEN) + turbulenceNoiseView.initShader(baseType = SIMPLEX_NOISE, newConfig) + + val newShader = turbulenceNoiseView.turbulenceNoiseShader + assertThat(newShader).isNotNull() + assertThat(newShader).isEqualTo(shader) + } + + @Test + fun initShader_changesBaseType_createsNewShader() { + val config = TurbulenceNoiseAnimationConfig() + val turbulenceNoiseView = TurbulenceNoiseView(context, null) + turbulenceNoiseView.initShader(baseType = SIMPLEX_NOISE, config) + + val shader = turbulenceNoiseView.turbulenceNoiseShader + assertThat(shader).isNotNull() + + turbulenceNoiseView.initShader(baseType = SIMPLEX_NOISE_FRACTAL, config) + + val newShader = turbulenceNoiseView.turbulenceNoiseShader + assertThat(newShader).isNotNull() + assertThat(newShader).isNotEqualTo(shader) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index 83439f0fc60d..8f4cbafecbcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -104,9 +104,9 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); - mManager = new ThemeOverlayApplier(mOverlayManager, - MoreExecutors.directExecutor(), - LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) { + mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(), + LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager, + MoreExecutors.directExecutor()) { @Override protected OverlayManagerTransaction.Builder getTransactionBuilder() { return mTransactionBuilder; @@ -179,7 +179,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_allEnabledExclusively() { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), - TEST_USER_HANDLES); + TEST_USER_HANDLES, null); verify(mOverlayManager).commit(any()); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { @@ -191,7 +191,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), - TEST_USER_HANDLES); + TEST_USER_HANDLES, null); for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) { if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { @@ -208,7 +208,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { public void allCategoriesSpecified_enabledForAllUserHandles() { Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), - userHandles); + userHandles, null); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), @@ -225,7 +225,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), - userHandles); + userHandles, null); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), @@ -239,7 +239,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { mock(FabricatedOverlay.class) }; mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, - TEST_USER.getIdentifier(), TEST_USER_HANDLES); + TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); for (FabricatedOverlay overlay : pendingCreation) { verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay)); @@ -253,7 +253,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID); mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), - TEST_USER_HANDLES); + TEST_USER_HANDLES, null); for (OverlayIdentifier overlayPackage : categoryToPackage.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), @@ -270,7 +270,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void zeroCategoriesSpecified_allDisabled() { mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(), - TEST_USER_HANDLES); + TEST_USER_HANDLES, null); for (String category : THEME_CATEGORIES) { verify(mTransactionBuilder).setEnabled( @@ -285,7 +285,7 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category")); mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), - TEST_USER_HANDLES); + TEST_USER_HANDLES, null); verify(mTransactionBuilder, never()).setEnabled( eq(new OverlayIdentifier("com.example.blah.category")), eq(false), diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index b58a41c89a4e..c02583ae6f0b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.UiModeManager; import android.app.WallpaperColors; import android.app.WallpaperManager; @@ -129,6 +130,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private UiModeManager mUiModeManager; + @Mock + private ActivityManager mActivityManager; @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver; @Captor @@ -164,7 +167,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) { + mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) { @VisibleForTesting protected boolean isNightMode() { return false; @@ -224,7 +227,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -249,7 +252,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastReceiver.getValue().onReceive(null, intent); mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -263,7 +266,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any()); // Should not change theme after changing wallpapers, if intent doesn't have // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true. @@ -272,7 +275,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -294,7 +297,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -333,7 +336,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { .isFalse(); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -367,8 +370,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { assertThat(updatedSetting.getValue().contains( "android.theme.customization.color_both\":\"0")).isTrue(); - verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -423,7 +425,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { assertThat(updatedSetting.getValue().contains( "android.theme.customization.color_both\":\"1")).isTrue(); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -492,7 +494,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { "android.theme.customization.color_both\":\"1")).isTrue(); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -523,7 +525,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index")) .isFalse(); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -554,7 +556,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index")) .isFalse(); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -587,7 +589,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { anyInt()); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -620,7 +622,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture()); // Apply overlay by existing theme from secure setting - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -653,7 +655,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -675,7 +677,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays = ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any()); // Assert that we received secondary user colors assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -689,7 +691,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_PROFILE_ADDED) .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -700,7 +702,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { new Intent(Intent.ACTION_PROFILE_ADDED) .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -711,7 +713,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { new Intent(Intent.ACTION_PROFILE_ADDED) .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -723,7 +725,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { (new Intent(Intent.ACTION_PROFILE_ADDED)) .putExtra(Intent.EXTRA_USER, PRIVATE_USER_HANDLE)); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @@ -737,7 +739,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); // Regression test: null events should not reset the internal state and allow colors to be // applied again. @@ -747,11 +749,11 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastReceiver.getValue().onReceive(null, intent); mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), - any()); + any(), any()); mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), - any()); + any(), any()); } @Test @@ -770,7 +772,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) { + mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) { @VisibleForTesting protected boolean isNightMode() { return false; @@ -791,7 +793,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture()); // Colors were applied during controller initialization. - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); clearInvocations(mThemeOverlayApplier); } @@ -810,7 +812,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) { + mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) { @VisibleForTesting protected boolean isNightMode() { return false; @@ -831,7 +833,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture()); // Colors were applied during controller initialization. - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); clearInvocations(mThemeOverlayApplier); WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), @@ -853,12 +855,12 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { // Defers event because we already have initial colors. verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); // Then event happens after setup phase is over. when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); mDeviceProvisionedListener.getValue().onUserSetupChanged(); - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -881,11 +883,11 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -907,10 +909,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep(); - verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); } @Test @@ -930,7 +932,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -949,19 +951,19 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(startingColors, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); clearInvocations(mThemeOverlayApplier); // Set to the same colors. mColorsListener.getValue().onColorsChanged(sameColors, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM); verify(mThemeOverlayApplier, never()) - .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any()); // Verify that no change resulted. mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep(); verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), - any()); + any(), any()); } @Test @@ -975,7 +977,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(FabricatedOverlay[].class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(any(), themeOverlays.capture(), anyInt(), any()); + .applyCurrentUserOverlays(any(), themeOverlays.capture(), anyInt(), any(), any()); FabricatedOverlay[] overlays = themeOverlays.getValue(); FabricatedOverlay accents = overlays[0]; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java index 82631744d12d..fccb936a3bc8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -37,10 +37,10 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -68,7 +68,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { @Mock private ActivityStarter mActivityStarter; @Mock - private ActivityLaunchAnimator.Controller mAnimationController; + private ActivityTransitionAnimator.Controller mAnimationController; @Captor private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor; @Captor @@ -219,7 +219,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { public void getQuickAccessUiIntent_hasCards_noPendingIntent_startsWalletActivity() { mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true); verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true), - any(ActivityLaunchAnimator.Controller.class), eq(true)); + any(ActivityTransitionAnimator.Controller.class), eq(true)); Intent intent = mIntentCaptor.getValue(); assertEquals(intent.getAction(), Intent.ACTION_VIEW); assertEquals( @@ -231,7 +231,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { public void getQuickAccessUiIntent_noCards_noPendingIntent_startsWalletActivity() { mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, false); verify(mActivityStarter).postStartActivityDismissingKeyguard(mIntentCaptor.capture(), eq(0), - any(ActivityLaunchAnimator.Controller.class)); + any(ActivityTransitionAnimator.Controller.class)); Intent intent = mIntentCaptor.getValue(); assertEquals(intent.getAction(), Intent.ACTION_VIEW); assertEquals( @@ -254,7 +254,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { }).when(mQuickAccessWalletClient).getWalletPendingIntent(any(), any()); mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true); verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntentCaptor.capture(), - any(ActivityLaunchAnimator.Controller.class)); + any(ActivityTransitionAnimator.Controller.class)); PendingIntent pendingIntent = mPendingIntentCaptor.getValue(); Intent intent = pendingIntent.getIntent(); assertEquals(intent.getAction(), Intent.ACTION_VIEW); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index d45a9a944da2..8d933dcdb9d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -176,9 +176,11 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleEducationController; import com.android.wm.shell.bubbles.BubbleEntry; +import com.android.wm.shell.bubbles.BubbleExpandedViewManager; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubbleStackView; +import com.android.wm.shell.bubbles.BubbleTaskView; import com.android.wm.shell.bubbles.BubbleViewInfoTask; import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.Bubbles; @@ -195,6 +197,7 @@ import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; @@ -214,6 +217,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.concurrent.Executor; import kotlinx.coroutines.test.TestScope; @@ -1416,7 +1420,9 @@ public class BubblesTest extends SysuiTestCase { .thenReturn(userContext); BubbleViewInfoTask.BubbleViewInfo info = BubbleViewInfoTask.BubbleViewInfo.populate(context, - mBubbleController, + BubbleExpandedViewManager.fromBubbleController(mBubbleController), + () -> new BubbleTaskView(mock(TaskView.class), mock(Executor.class)), + mPositioner, mBubbleController.getStackView(), new BubbleIconFactory(mContext, mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_size), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt index 128f58bf9751..66c9afba0cd6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt @@ -18,4 +18,4 @@ package com.android.systemui.animation import com.android.systemui.kosmos.Kosmos -val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() } +val Kosmos.activityTransitionAnimator by Kosmos.Fixture { ActivityTransitionAnimator() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt new file mode 100644 index 000000000000..5485f79629e8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.data.repository + +import android.app.admin.devicePolicyManager +import com.android.systemui.broadcast.broadcastDispatcher +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.util.settings.fakeSettings + +val Kosmos.communalSettingsRepository: CommunalSettingsRepository by + Kosmos.Fixture { + CommunalSettingsRepositoryImpl( + bgDispatcher = testDispatcher, + featureFlagsClassic = featureFlagsClassic, + secureSettings = fakeSettings, + broadcastDispatcher = broadcastDispatcher, + devicePolicyManager = devicePolicyManager, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt index cccd90832326..ae7d87783b7c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalRepository( applicationScope: CoroutineScope, - override var isCommunalEnabled: Boolean = true, override val desiredScene: MutableStateFlow<CommunalSceneKey> = MutableStateFlow(CommunalSceneKey.DEFAULT), ) : CommunalRepository { @@ -40,21 +39,10 @@ class FakeCommunalRepository( _transitionState.value = transitionState } - fun setIsCommunalEnabled(value: Boolean) { - isCommunalEnabled = value - } - private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) { _isCommunalHubShowing.value = isCommunalHubShowing } - - private val _communalEnabledState: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val communalEnabledState: StateFlow<Boolean> = _communalEnabledState - - fun setCommunalEnabledState(enabled: Boolean) { - _communalEnabledState.value = enabled - } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt index 7301404eada0..fab64e38e1f8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt @@ -36,7 +36,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : } } - override fun deleteWidgetFromDb(widgetId: Int) { + override fun deleteWidget(widgetId: Int) { if (_communalWidgets.value.none { it.appWidgetId == widgetId }) { return } @@ -44,10 +44,6 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : _communalWidgets.value = _communalWidgets.value.filter { it.appWidgetId != widgetId } } - override fun deleteWidgetFromHost(widgetId: Int) { - deleteWidgetFromDb(widgetId) - } - private fun onConfigured(id: Int, providerInfo: AppWidgetProviderInfo, priority: Int) { _communalWidgets.value += listOf(CommunalWidgetContentModel(id, providerInfo, priority)) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt index c47f020a3b83..f7e9a117aa77 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt @@ -27,7 +27,6 @@ import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.smartspace.data.repository.smartspaceRepository -import com.android.systemui.user.data.repository.userRepository import com.android.systemui.util.mockito.mock val Kosmos.communalInteractor by Fixture { @@ -38,12 +37,12 @@ val Kosmos.communalInteractor by Fixture { mediaRepository = communalMediaRepository, communalPrefsRepository = communalPrefsRepository, smartspaceRepository = smartspaceRepository, - userRepository = userRepository, appWidgetHost = mock(), keyguardInteractor = keyguardInteractor, editWidgetsActivityStarter = editWidgetsActivityStarter, logBuffer = logcatLogBuffer("CommunalInteractor"), tableLogBuffer = mock(), + communalSettingsInteractor = communalSettingsInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt new file mode 100644 index 000000000000..b4773f69f1c5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.domain.interactor + +import com.android.systemui.communal.data.repository.communalSettingsRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.user.domain.interactor.selectedUserInteractor +import com.android.systemui.util.mockito.mock + +val Kosmos.communalSettingsInteractor by Fixture { + CommunalSettingsInteractor( + bgScope = applicationCoroutineScope, + repository = communalSettingsRepository, + userInteractor = selectedUserInteractor, + tableLogBuffer = mock(), + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt index 9776b436555d..00fdceda01d1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt @@ -31,6 +31,7 @@ val Kosmos.communalTutorialInteractor by keyguardInteractor = keyguardInteractor, communalRepository = communalRepository, communalInteractor = communalInteractor, + communalSettingsInteractor = communalSettingsInteractor, tableLogBuffer = mock(), ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerProviderKosmos.kt index c3db34bdddb7..dc5a2f8cdc4e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerProviderKosmos.kt @@ -19,5 +19,5 @@ package com.android.systemui.statusbar.notification import com.android.systemui.kosmos.Kosmos import com.android.systemui.util.mockito.mock -var Kosmos.notificationLaunchAnimatorControllerProvider by +var Kosmos.notificationTransitionAnimatorControllerProvider by Kosmos.Fixture { mock<NotificationLaunchAnimatorControllerProvider>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderControllerKosmos.kt new file mode 100644 index 000000000000..3bbac3236885 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.render + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.silentHeaderController by Kosmos.Fixture { mock<SectionHeaderController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt index 489598c4dba9..2de26f13ad73 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt @@ -22,10 +22,11 @@ import com.android.systemui.common.ui.configurationState import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.statusbar.notification.collection.render.silentHeaderController import com.android.systemui.statusbar.notification.icon.ui.viewbinder.notificationIconContainerShelfViewBinder import com.android.systemui.statusbar.notification.notificationActivityStarter -import com.android.systemui.statusbar.notification.stack.ui.view.notificationStatsLogger import com.android.systemui.statusbar.notification.stack.displaySwitchNotificationsHiderTracker +import com.android.systemui.statusbar.notification.stack.ui.view.notificationStatsLogger import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel import com.android.systemui.statusbar.phone.notificationIconAreaController import java.util.Optional @@ -42,5 +43,6 @@ val Kosmos.notificationListViewBinder by Fixture { hiderTracker = displaySwitchNotificationsHiderTracker, nicBinder = notificationIconContainerShelfViewBinder, notificationActivityStarter = { notificationActivityStarter }, + silentHeaderController = silentHeaderController, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt index 37b2b765c9bd..25e3eac0b4b7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt @@ -17,11 +17,9 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.systemui.keyguard.domain.interactor.keyguardInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.power.domain.interactor.powerInteractor -import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor @@ -40,7 +38,6 @@ val Kosmos.notificationListViewModel by Fixture { Optional.of(notificationListLoggerViewModel), activeNotificationsInteractor, keyguardInteractor, - keyguardTransitionInteractor, powerInteractor, remoteInputInteractor, seenNotificationsInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index 8042b5c7bf3e..41c11ad61c7f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -23,7 +23,7 @@ import android.service.dream.dreamManagerInterface import com.android.internal.logging.metricsLogger import com.android.internal.widget.lockPatternUtils import com.android.systemui.activityIntentHelper -import com.android.systemui.animation.activityLaunchAnimator +import com.android.systemui.animation.activityTransitionAnimator import com.android.systemui.assist.assistManager import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos @@ -35,7 +35,7 @@ import com.android.systemui.shade.shadeController import com.android.systemui.shade.shadeViewController import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider -import com.android.systemui.statusbar.notification.notificationLaunchAnimatorControllerProvider +import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider import com.android.systemui.statusbar.notification.row.onUserInteractionCallback import com.android.systemui.statusbar.notificationClickNotifier import com.android.systemui.statusbar.notificationLockscreenUserManager @@ -78,9 +78,9 @@ val Kosmos.statusBarNotificationActivityStarter by notificationPresenter, shadeViewController, notificationShadeWindowController, - activityLaunchAnimator, + activityTransitionAnimator, shadeAnimationInteractor, - notificationLaunchAnimatorControllerProvider, + notificationTransitionAnimatorControllerProvider, launchFullScreenIntentProvider, powerInteractor, userTracker, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt new file mode 100644 index 000000000000..bcb584858e32 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.util.settings + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.fakeSettings: FakeSettings by Fixture { FakeSettings() } diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index a5ecd20b8b4f..75c5a49e9720 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -155,6 +155,11 @@ android.view.Display$HdrCapabilities android.view.Display$Mode android.view.DisplayInfo +android.telephony.ActivityStatsTechSpecificInfo +android.telephony.CellSignalStrength +android.telephony.ModemActivityInfo +android.telephony.ServiceState + com.android.server.LocalServices com.android.server.power.stats.BatteryStatsImpl diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index a11cf8ca1345..26c1bc904a1a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -928,17 +928,11 @@ public class AccessibilityWindowManager { * Dumps all {@link AccessibilityWindowInfo}s here. */ void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) { - pw.append("Global Info [ "); - pw.println("Top focused display Id = " + mTopFocusedDisplayId); - pw.println(" Active Window Id = " + mActiveWindowId); - pw.println(" Top Focused Window Id = " + mTopFocusedWindowId); - pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId - + " ]"); if (mIsProxy) { pw.println("Proxy accessibility focused window = " + mProxyDisplayAccessibilityFocusedWindow); + pw.println(); } - pw.println(); if (mWindows != null) { final int windowCount = mWindows.size(); for (int j = 0; j < windowCount; j++) { @@ -2201,6 +2195,13 @@ public class AccessibilityWindowManager { * Dumps all {@link AccessibilityWindowInfo}s here. */ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { + pw.append("Global Info [ "); + pw.println("Top focused display Id = " + mTopFocusedDisplayId); + pw.println(" Active Window Id = " + mActiveWindowId); + pw.println(" Top Focused Window Id = " + mTopFocusedWindowId); + pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId + + " ]"); + pw.println(); final int count = mDisplayWindowsObservers.size(); for (int i = 0; i < count; i++) { final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index fd8ab9683831..e1291e5f75ec 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -451,7 +451,7 @@ final class AutofillManagerServiceImpl final Session.SaveResult saveResult = session.showSaveLocked(); - session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason); + session.logContextCommittedLocked(saveResult.getNoSaveUiReason(), commitReason); if (saveResult.isLogSaveShown()) { session.logSaveUiShown(); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2ff23ee17bfd..b89e0d8c72df 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3086,6 +3086,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * when necessary. */ public void logContextCommitted() { + if (sVerbose) { + Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN + + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE); + } mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, Event.NO_SAVE_UI_REASON_NONE, COMMIT_REASON_UNKNOWN)); @@ -3094,16 +3098,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} - * when necessary. + * when necessary. Note that it could be called before save UI is shown and the session is + * committed. * * @param saveDialogNotShowReason The reason why a save dialog was not shown. * @param commitReason The reason why context is committed. */ - public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason, + + @GuardedBy("mLock") + public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { + if (sVerbose) { + Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason + + " no_save_reason:" + saveDialogNotShowReason); + } mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, saveDialogNotShowReason, commitReason)); - logAllEvents(commitReason); + + mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); + mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); + mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason, @@ -3159,6 +3173,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { + if (sVerbose) { + Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason + + " no_save_reason:" + saveDialogNotShowReason); + } final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; @@ -3335,7 +3353,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, mComponentName, mCompatMode, saveDialogNotShowReason); - logAllEvents(commitReason); + mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); + mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); + mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason); } /** @@ -3776,11 +3796,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - if (sDebug) { - Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " - + id + "!"); - } - final IAutoFillManagerClient client = getClient(); mPendingSaveUi = new PendingUi(new Binder(), id, client); @@ -3812,6 +3827,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } mSessionFlags.mShowingSaveUi = true; + if (sDebug) { + Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " + + id + "!"); + } return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } @@ -6386,6 +6405,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void logAllEvents(@AutofillCommitReason int val) { + if (sVerbose) { + Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val); + } mSessionCommittedEventLogger.maybeSetCommitReason(val); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSessionCommittedEventLogger.maybeSetSessionDurationMillis( @@ -6411,6 +6433,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") RemoteFillService destroyLocked() { // Log unlogged events. + if (sVerbose) { + Slog.v(TAG, "destroyLocked for session: " + id); + } logAllEvents(COMMIT_REASON_SESSION_DESTROYED); if (mDestroyed) { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 0054bc87af77..b43f1a93f183 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -1305,6 +1305,8 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore.dump(out); mDevicePresenceMonitor.dump(out); mCompanionAppController.dump(out); + mTransportManager.dump(out); + mSystemDataTransferRequestStore.dump(out); } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java index 51c5fd69cdf2..c4c80f907b3a 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java @@ -48,6 +48,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -303,6 +304,32 @@ public class SystemDataTransferRequestStore { } } + + + /** + * Dumps current system data transfer request states. + */ + public void dump(@NonNull PrintWriter out) { + synchronized (mLock) { + out.append("System Data Transfer Requests (Cached): "); + if (mCachedPerUser.size() == 0) { + out.append("<empty>\n"); + } else { + out.append("\n"); + for (int i = 0; i < mCachedPerUser.size(); i++) { + final int userId = mCachedPerUser.keyAt(i); + for (SystemDataTransferRequest request : mCachedPerUser.get(userId)) { + out.append(" u") + .append(String.valueOf(userId)) + .append(" -> ") + .append(request.toString()) + .append('\n'); + } + } + } + } + } + private void writeRequestsToXml(@NonNull TypedXmlSerializer serializer, @Nullable Collection<SystemDataTransferRequest> requests) throws IOException { serializer.startTag(null, XML_TAG_REQUESTS); diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index 3e45626d9799..3861f99eb03c 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -36,6 +36,7 @@ import com.android.server.companion.AssociationStore; import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -225,6 +226,25 @@ public class CompanionTransportManager { } /** + * Dumps current list of active transports. + */ + public void dump(@NonNull PrintWriter out) { + synchronized (mTransports) { + out.append("System Data Transports: "); + if (mTransports.size() == 0) { + out.append("<empty>\n"); + } else { + out.append("\n"); + for (int i = 0; i < mTransports.size(); i++) { + final int associationId = mTransports.keyAt(i); + final Transport transport = mTransports.get(associationId); + out.append(" ").append(transport.toString()).append('\n'); + } + } + } + } + + /** * @hide */ public void enableSecureTransport(boolean enabled) { diff --git a/services/companion/java/com/android/server/companion/transport/RawTransport.java b/services/companion/java/com/android/server/companion/transport/RawTransport.java index ca169aac6a37..05703ce21ca4 100644 --- a/services/companion/java/com/android/server/companion/transport/RawTransport.java +++ b/services/companion/java/com/android/server/companion/transport/RawTransport.java @@ -94,6 +94,13 @@ class RawTransport extends Transport { } } + @Override + public String toString() { + return "RawTransport{" + + "mAssociationId=" + mAssociationId + + '}'; + } + private void receiveMessage() throws IOException { synchronized (mRemoteIn) { final byte[] headerBytes = new byte[HEADER_LENGTH]; diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java index 6e906ebe887a..1e95e65848a5 100644 --- a/services/companion/java/com/android/server/companion/transport/SecureTransport.java +++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java @@ -152,4 +152,12 @@ class SecureTransport extends Transport implements SecureChannel.Callback { close(); } } + + @Override + public String toString() { + return "SecureTransport{" + + "mAssociationId=" + mAssociationId + + ", mSecureChannel=" + mSecureChannel + + '}'; + } } diff --git a/services/core/Android.bp b/services/core/Android.bp index 89896c3735b6..469209976f5f 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -214,6 +214,7 @@ java_library_static { "policy_flags_lib", "net_flags_lib", "stats_flags_lib", + "core_os_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index 627a62ee0496..34c3d7ec8433 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -246,16 +246,6 @@ public class AdbDebuggingManager { @Override public void run() { - if (mGuid.isEmpty()) { - Slog.e(TAG, "adbwifi guid was not set"); - return; - } - mPort = native_pairing_start(mGuid, mPairingCode); - if (mPort <= 0 || mPort > 65535) { - Slog.e(TAG, "Unable to start pairing server"); - return; - } - // Register the mdns service NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setServiceName(mServiceName); @@ -288,6 +278,28 @@ public class AdbDebuggingManager { mHandler.sendMessage(message); } + @Override + public void start() { + /* + * If a user is fast enough to click cancel, native_pairing_cancel can be invoked + * while native_pairing_start is running which run the destruction of the object + * while it is being constructed. Here we start the pairing server on foreground + * Thread so native_pairing_cancel can never be called concurrently. Then we let + * the pairing server run on a background Thread. + */ + if (mGuid.isEmpty()) { + Slog.e(TAG, "adbwifi guid was not set"); + return; + } + mPort = native_pairing_start(mGuid, mPairingCode); + if (mPort <= 0) { + Slog.e(TAG, "Unable to start pairing server"); + return; + } + + super.start(); + } + public void cancelPairing() { native_pairing_cancel(); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a5531ae65bbe..2750344b487e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -187,6 +187,7 @@ import static com.android.server.wm.ActivityTaskManagerService.DUMP_TOP_RESUMED_ import static com.android.server.wm.ActivityTaskManagerService.DUMP_VISIBLE_ACTIVITIES; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString; +import static com.android.systemui.shared.Flags.enableHomeDelay; import android.Manifest; import android.Manifest.permission; @@ -521,6 +522,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -921,6 +923,15 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") final ComponentAliasResolver mComponentAliasResolver; + private static final long HOME_LAUNCH_TIMEOUT_MS = 15000; + private final AtomicBoolean mHasHomeDelay = new AtomicBoolean(false); + + /** + * Tracks all users with computed color resources by ThemeOverlaycvontroller + */ + @GuardedBy("this") + private final Set<Integer> mThemeOverlayReadiness = new HashSet<>(); + /** * Tracks association information for a particular package along with debuggability. * <p> Associations for a package A are allowed to package B if B is part of the @@ -2332,6 +2343,7 @@ public class ActivityManagerService extends IActivityManager.Stub mService.startBroadcastObservers(); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { mService.mPackageWatchdog.onPackagesReady(); + mService.setHomeTimeout(); } } @@ -5304,6 +5316,59 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * Starts Home if there is no completion signal from ThemeOverlayController + */ + private void setHomeTimeout() { + if (enableHomeDelay() && mHasHomeDelay.compareAndSet(false, true)) { + mHandler.postDelayed(() -> { + if (!getThemeOverlayReadiness()) { + Slog.d(TAG, + "ThemeHomeDelay: ThemeOverlayController not responding, launching " + + "Home after " + + HOME_LAUNCH_TIMEOUT_MS + "ms"); + setThemeOverlayReady(true); + } + }, HOME_LAUNCH_TIMEOUT_MS); + } + } + + /** + * Used by ThemeOverlayController to notify all listeners for + * color palette readiness. + * @hide + */ + @Override + public void setThemeOverlayReady(boolean readiness) { + enforceCallingPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY, + "setThemeOverlayReady"); + + int currentUserId = mUserController.getCurrentUserId(); + + boolean updateReadiness; + synchronized (mThemeOverlayReadiness) { + updateReadiness = readiness ? mThemeOverlayReadiness.add(currentUserId) + : mThemeOverlayReadiness.remove(currentUserId); + } + + if (updateReadiness && readiness && enableHomeDelay()) { + mAtmInternal.startHomeOnAllDisplays(currentUserId, "setThemeOverlayReady"); + } + } + + /** + * Returns current state of ThemeOverlayController color + * palette readiness. + * + * @hide + */ + public boolean getThemeOverlayReadiness() { + int uid = mUserController.getCurrentUserId(); + synchronized (mThemeOverlayReadiness) { + return mThemeOverlayReadiness.contains(uid); + } + } + final void ensureBootCompleted() { boolean booting; boolean enableScreen; @@ -18033,6 +18098,10 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.onUserStopped(userId); // Clean up various services by removing the user mBatteryStatsService.onUserRemoved(userId); + + synchronized (mThemeOverlayReadiness) { + mThemeOverlayReadiness.remove(userId); + } } @Override @@ -19391,6 +19460,11 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.clearApplicationUserData(packageName, keepState, isRestore, observer, userId); } + + @Override + public boolean getThemeOverlayReadiness() { + return ActivityManagerService.this.getThemeOverlayReadiness(); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index bbbba26303a7..04deb0271c06 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -12458,6 +12458,20 @@ public class AudioService extends IAudioService.Stub return app; } + /** + * Retrieves all audioMixes registered with the AudioPolicyManager + * @return list of registered audio mixes + */ + public List<AudioMix> getRegisteredPolicyMixes() { + if (!android.media.audiopolicy.Flags.audioMixTestApi()) { + return Collections.emptyList(); + } + + synchronized (mAudioPolicies) { + return mAudioSystem.getRegisteredPolicyMixes(); + } + } + public int addMixForPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb) { if (DEBUG_AP) { Log.d(TAG, "addMixForPolicy for " + pcb.asBinder() + " with config:" + policyConfig); } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 4f46dd13f973..49ab19a816dc 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -27,6 +27,7 @@ import android.media.ISoundDose; import android.media.ISoundDoseCallback; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMixingRule; +import android.media.audiopolicy.Flags; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -42,6 +43,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; @@ -602,6 +604,23 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, } /** + * @return a list of AudioMixes that are registered in the audio policy manager. + */ + public List<AudioMix> getRegisteredPolicyMixes() { + if (!Flags.audioMixTestApi()) { + return Collections.emptyList(); + } + + List<AudioMix> audioMixes = new ArrayList<>(); + int result = AudioSystem.getRegisteredPolicyMixes(audioMixes); + if (result != AudioSystem.SUCCESS) { + throw new IllegalStateException( + "Cannot fetch registered policy mixes. Result: " + result); + } + return Collections.unmodifiableList(audioMixes); + } + + /** * Update already {@link AudioMixingRule}-s for already registered {@link AudioMix}-es. * * @param mixes - array of registered {@link AudioMix}-es to update. diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java index 92218b1023c4..199db8c48c7c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java @@ -27,9 +27,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; /** * Allows clients (such as keyguard) to register for notifications on when biometric lockout @@ -42,7 +41,7 @@ public class LockoutResetDispatcher implements IBinder.DeathRecipient { private final Context mContext; @VisibleForTesting - final List<ClientCallback> mClientCallbacks = new ArrayList<>(); + final ConcurrentLinkedQueue<ClientCallback> mClientCallbacks = new ConcurrentLinkedQueue<>(); private static class ClientCallback { private static final long WAKELOCK_TIMEOUT_MS = 2000; diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java index c260f10b61a6..6a6e6ab23687 100644 --- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java +++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java @@ -47,5 +47,19 @@ public abstract class GrammaticalInflectionManagerInternal { * @see Configuration#getGrammaticalGender */ public abstract @Configuration.GrammaticalGender int getSystemGrammaticalGender(int userId); + + /** + * Retrieve the system grammatical gender. + * + * @return the value of grammatical gender + * + */ + public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender( + Configuration configuration); + + /** + * Whether the package can get the system grammatical gender or not. + */ + public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName); } diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java index 6eb7e9559b8d..d01f54f09679 100644 --- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java +++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java @@ -22,17 +22,21 @@ import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission; import android.annotation.Nullable; +import android.app.ActivityTaskManager; import android.app.GrammaticalInflectionManager; import android.app.IGrammaticalInflectionManager; import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; import android.os.Binder; import android.os.Environment; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemProperties; +import android.os.Trace; import android.permission.PermissionManager; import android.util.AtomicFile; import android.util.Log; @@ -43,6 +47,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; +import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; @@ -71,6 +76,7 @@ public class GrammaticalInflectionService extends SystemService { private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection"; private static final String GRAMMATICAL_INFLECTION_ENABLED = "i18n.grammatical_Inflection.enabled"; + private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender"; private final GrammaticalInflectionBackupHelper mBackupHelper; private final ActivityTaskManagerInternal mActivityTaskManagerInternal; @@ -121,16 +127,16 @@ public class GrammaticalInflectionService extends SystemService { @Override public void setSystemWideGrammaticalGender(int grammaticalGender, int userId) { checkCallerIsSystem(); - checkSystemTermsOfAddressIsEnabled(); GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender, userId); } @Override public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) { - checkSystemTermsOfAddressIsEnabled(); - return GrammaticalInflectionService.this.getSystemGrammaticalGender(attributionSource, - userId); + return canGetSystemGrammaticalGender(attributionSource) + ? GrammaticalInflectionService.this.getSystemGrammaticalGender( + attributionSource, userId) + : GRAMMATICAL_GENDER_NOT_SPECIFIED; } @Override @@ -159,9 +165,33 @@ public class GrammaticalInflectionService extends SystemService { @Override public int getSystemGrammaticalGender(int userId) { - checkCallerIsSystem(); - return GrammaticalInflectionService.this.getSystemGrammaticalGender( - mContext.getAttributionSource(), userId); + return checkSystemTermsOfAddressIsEnabled() + ? GrammaticalInflectionService.this.getSystemGrammaticalGender( + mContext.getAttributionSource(), userId) + : GRAMMATICAL_GENDER_NOT_SPECIFIED; + } + + @Override + public int retrieveSystemGrammaticalGender(Configuration configuration) { + int systemGrammaticalGender = getSystemGrammaticalGender(mContext.getUserId()); + // Retrieve the grammatical gender from system property, set it into + // configuration which will get updated later if the grammatical gender raw value of + // current configuration is {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}. + if (configuration.getGrammaticalGenderRaw() + == Configuration.GRAMMATICAL_GENDER_UNDEFINED + || systemGrammaticalGender <= Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) { + systemGrammaticalGender = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY, + Configuration.GRAMMATICAL_GENDER_UNDEFINED); + } + return systemGrammaticalGender; + } + + @Override + public boolean canGetSystemGrammaticalGender(int uid, String packageName) { + AttributionSource attributionSource = new AttributionSource.Builder( + uid).setPackageName(packageName).build(); + return GrammaticalInflectionService.this.canGetSystemGrammaticalGender( + attributionSource); } } @@ -202,11 +232,20 @@ public class GrammaticalInflectionService extends SystemService { } protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) { + Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender"); if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains( grammaticalGender)) { throw new IllegalArgumentException("Unknown grammatical gender"); } + if (!checkSystemTermsOfAddressIsEnabled()) { + if (grammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED) { + return; + } + Log.d(TAG, "Clearing the system grammatical gender setting"); + grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED; + } + synchronized (mLock) { final File file = getGrammaticalGenderFile(userId); final AtomicFile atomicFile = new AtomicFile(file); @@ -224,6 +263,15 @@ public class GrammaticalInflectionService extends SystemService { throw new RuntimeException(e); } } + + try { + Configuration config = new Configuration(); + config.setGrammaticalGender(grammaticalGender); + ActivityTaskManager.getService().updateConfiguration(config); + } catch (RemoteException e) { + Log.w(TAG, "Can not update configuration", e); + } + Trace.endSection(); } public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) { @@ -233,34 +281,9 @@ public class GrammaticalInflectionService extends SystemService { return GRAMMATICAL_GENDER_NOT_SPECIFIED; } - int callingUid = Binder.getCallingUid(); - if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) != callingUid) { - Log.d(TAG, - "Package " + packageName + " does not belong to the calling uid " + callingUid); - return GRAMMATICAL_GENDER_NOT_SPECIFIED; - } - - if (!checkSystemGrammaticalGenderPermission(mPermissionManager, attributionSource)) { - return GRAMMATICAL_GENDER_NOT_SPECIFIED; - } - synchronized (mLock) { - final File file = getGrammaticalGenderFile(userId); - if (!file.exists()) { - Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file."); - return GRAMMATICAL_GENDER_NOT_SPECIFIED; - } - - if (mGrammaticalGenderCache.indexOfKey(userId) < 0) { - try { - InputStream in = new FileInputStream(file); - final TypedXmlPullParser parser = Xml.resolvePullParser(in); - mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser)); - } catch (IOException | XmlPullParserException e) { - Log.e(TAG, "Failed to parse XML configuration from " + file, e); - } - } - return mGrammaticalGenderCache.get(userId); + int grammaticalGender = mGrammaticalGenderCache.get(userId); + return grammaticalGender < 0 ? GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender; } } @@ -311,9 +334,39 @@ public class GrammaticalInflectionService extends SystemService { } } - private void checkSystemTermsOfAddressIsEnabled() { + private boolean checkSystemTermsOfAddressIsEnabled() { if (!systemTermsOfAddressEnabled()) { - throw new RuntimeException("The flag must be enabled to allow calling the API."); + Log.d(TAG, "The flag must be enabled to allow calling the API."); + return false; } + return true; + } + + private boolean canGetSystemGrammaticalGender(AttributionSource attributionSource) { + return checkSystemTermsOfAddressIsEnabled() && checkSystemGrammaticalGenderPermission( + mPermissionManager, attributionSource); + } + + @Override + public void onUserUnlocked(TargetUser user) { + IoThread.getHandler().post(() -> { + int userId = user.getUserIdentifier(); + final File file = getGrammaticalGenderFile(userId); + synchronized (mLock) { + if (!file.exists()) { + Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file."); + return; + } + if (mGrammaticalGenderCache.indexOfKey(userId) < 0) { + try { + InputStream in = new FileInputStream(file); + final TypedXmlPullParser parser = Xml.resolvePullParser(in); + mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser)); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Failed to parse XML configuration from " + file, e); + } + } + } + }); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index e0e825d9147a..c8c662387d16 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1366,6 +1366,12 @@ public class HdmiControlService extends SystemService { // we don't call onInitializeCecComplete() // since we reallocate the logical address only. onInitializeCecComplete(initiatedBy); + } else if (initiatedBy == INITIATED_BY_HOTPLUG + && mDisplayStatusCallback == null) { + // Force to update display status for hotplug event. + synchronized (mLock) { + announceHdmiControlStatusChange(mHdmiControlEnabled); + } } // We remove local devices here, instead of before the start of // address allocation, to prevent multiple local devices of the diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index e14f7c09770f..b6f48890c528 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; @@ -196,18 +197,20 @@ public final class PermissionHelper { boolean currentlyGranted = hasPermission(uid); if (grant && !currentlyGranted) { mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); } else if (!grant && currentlyGranted) { mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION, - Context.DEVICE_ID_DEFAULT, userId, TAG); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId, TAG); } int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; if (userSet) { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, flagMask, - FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, userId); + FLAG_PERMISSION_USER_SET, true, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); } else { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - flagMask, 0, true, Context.DEVICE_ID_DEFAULT, userId); + flagMask, 0, true, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, + userId); } } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); @@ -235,7 +238,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } catch (RemoteException e) { @@ -252,7 +255,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); return (flags & (PackageManager.FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_USER_FIXED)) != 0; } catch (RemoteException e) { @@ -269,7 +272,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); return (flags & (PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT | PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0; } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/os/Android.bp b/services/core/java/com/android/server/os/Android.bp new file mode 100644 index 000000000000..565dc3b644ea --- /dev/null +++ b/services/core/java/com/android/server/os/Android.bp @@ -0,0 +1,12 @@ +aconfig_declarations { + name: "core_os_flags", + package: "com.android.server.os", + srcs: [ + "*.aconfig", + ], +} + +java_aconfig_library { + name: "core_os_flags_lib", + aconfig_declarations: "core_os_flags", +} diff --git a/services/core/java/com/android/server/os/core_os_flags.aconfig b/services/core/java/com/android/server/os/core_os_flags.aconfig new file mode 100644 index 000000000000..cbc0d54046df --- /dev/null +++ b/services/core/java/com/android/server/os/core_os_flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.os" + +flag { + name: "proto_tombstone" + namespace: "proto_tombstone_ns" + description: "Use proto tombstones as source of truth for adding to dropbox" + bug: "323857385" +} diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java index 66ad7169d6ec..a56406ed1412 100644 --- a/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java +++ b/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java @@ -49,4 +49,10 @@ public interface PersistentDataBlockManagerInternal { /** Retrieves the UID that can access the persistent data partition. */ int getAllowedUid(); + + /** + * Attempt to deactivate Factory Reset Protection (FRP) without a secret. Returns true if + * successful, false if not. + */ + boolean deactivateFactoryResetProtectionWithoutSecret(); } diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java index b9b09fb0e84c..133fc8faf14d 100644 --- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java @@ -18,16 +18,31 @@ package com.android.server.pdb; import static com.android.internal.util.Preconditions.checkArgument; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.SYNC; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; + import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.os.ShellCommand; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; +import android.security.Flags; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; import android.text.TextUtils; @@ -56,10 +71,10 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.HexFormat; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -85,9 +100,14 @@ import java.util.concurrent.TimeUnit; * | --------------------------------------------| * | FRP data block length (4 bytes) | * | --------------------------------------------| - * | FRP data (variable length) | + * | FRP data (variable length; 100KB max) | * | --------------------------------------------| * | ... | + * | Empty space. | + * | ... | + * | --------------------------------------------| + * | FRP secret magic (8 bytes) | + * | FRP secret (32 bytes) | * | --------------------------------------------| * | Test mode data block (10000 bytes) | * | --------------------------------------------| @@ -127,6 +147,14 @@ public class PersistentDataBlockService extends SystemService { /** Maximum size of the FRP credential handle that can be stored. */ @VisibleForTesting static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4; + /** Size of the FRP mode deactivation secret, in bytes */ + @VisibleForTesting + static final int FRP_SECRET_SIZE = 32; + /** Magic value to identify the FRP secret is present. */ + @VisibleForTesting + static final byte[] FRP_SECRET_MAGIC = {(byte) 0xda, (byte) 0xc2, (byte) 0xfc, + (byte) 0xcd, (byte) 0xb9, 0x1b, 0x09, (byte) 0x88}; + /** * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header. */ @@ -145,21 +173,52 @@ public class PersistentDataBlockService extends SystemService { private static final String FLASH_LOCK_LOCKED = "1"; private static final String FLASH_LOCK_UNLOCKED = "0"; + /** + * Path to FRP secret stored on /data. This file enables automatic deactivation of FRP mode if + * it contains the current FRP secret. When /data is wiped in an untrusted reset this file is + * destroyed, blocking automatic deactivation. + */ + private static final String FRP_SECRET_FILE = "/data/system/frp_secret"; + + /** + * Path to temp file used when changing the FRP secret. + */ + private static final String FRP_SECRET_TMP_FILE = "/data/system/frp_secret_tmp"; + + public static final String BOOTLOADER_LOCK_STATE = "ro.boot.vbmeta.device_state"; + public static final String VERIFIED_BOOT_STATE = "ro.boot.verifiedbootstate"; + public static final int INIT_WAIT_TIMEOUT = 10; + private final Context mContext; private final String mDataBlockFile; private final boolean mIsFileBacked; private final Object mLock = new Object(); private final CountDownLatch mInitDoneSignal = new CountDownLatch(1); + private final String mFrpSecretFile; + private final String mFrpSecretTmpFile; private int mAllowedUid = -1; private long mBlockDeviceSize = -1; // Load lazily + private final boolean mFrpEnforced; + + /** + * FRP active state. When true (the default) we may have had an untrusted factory reset. In + * that case we block any updates of the persistent data block. To exit active state, it's + * necessary for some caller to provide the FRP secret. + */ + private boolean mFrpActive = false; + @GuardedBy("mLock") private boolean mIsWritable = true; public PersistentDataBlockService(Context context) { super(context); mContext = context; + mFrpEnforced = Flags.frpEnforcement(); + mFrpActive = mFrpEnforced; + mFrpSecretFile = FRP_SECRET_FILE; + mFrpSecretTmpFile = FRP_SECRET_TMP_FILE; if (SystemProperties.getBoolean(GSI_RUNNING_PROP, false)) { mIsFileBacked = true; mDataBlockFile = GSI_SANDBOX; @@ -171,12 +230,17 @@ public class PersistentDataBlockService extends SystemService { @VisibleForTesting PersistentDataBlockService(Context context, boolean isFileBacked, String dataBlockFile, - long blockDeviceSize) { + long blockDeviceSize, boolean frpEnabled, String frpSecretFile, + String frpSecretTmpFile) { super(context); mContext = context; mIsFileBacked = isFileBacked; mDataBlockFile = dataBlockFile; mBlockDeviceSize = blockDeviceSize; + mFrpEnforced = frpEnabled; + mFrpActive = mFrpEnforced; + mFrpSecretFile = frpSecretFile; + mFrpSecretTmpFile = frpSecretTmpFile; } private int getAllowedUid() { @@ -206,24 +270,35 @@ public class PersistentDataBlockService extends SystemService { // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY SystemServerInitThreadPool.submit(() -> { enforceChecksumValidity(); - formatIfOemUnlockEnabled(); + if (mFrpEnforced) { + automaticallyDeactivateFrpIfPossible(); + setOemUnlockEnabledProperty(doGetOemUnlockEnabled()); + // Set the SECURE_FRP_MODE flag, for backward compatibility with clients who use it. + // They should switch to calling #isFrpActive(). + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.SECURE_FRP_MODE, mFrpActive ? 1 : 0); + } else { + formatIfOemUnlockEnabled(); + } publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); - mInitDoneSignal.countDown(); + signalInitDone(); }, TAG + ".onStart"); } + @VisibleForTesting + void signalInitDone() { + mInitDoneSignal.countDown(); + } + + private void setOemUnlockEnabledProperty(boolean oemUnlockEnabled) { + setProperty(OEM_UNLOCK_PROP, oemUnlockEnabled ? "1" : "0"); + } + @Override public void onBootPhase(int phase) { // Wait for initialization in onStart to finish if (phase == PHASE_SYSTEM_SERVICES_READY) { - try { - if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) { - throw new IllegalStateException("Service " + TAG + " init timeout"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("Service " + TAG + " init interrupted", e); - } + waitForInitDoneSignal(); // The user responsible for FRP should exist by now. mAllowedUid = getAllowedUid(); LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService); @@ -231,6 +306,17 @@ public class PersistentDataBlockService extends SystemService { super.onBootPhase(phase); } + private void waitForInitDoneSignal() { + try { + if (!mInitDoneSignal.await(INIT_WAIT_TIMEOUT, TimeUnit.SECONDS)) { + throw new IllegalStateException("Service " + TAG + " init timeout"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Service " + TAG + " init interrupted", e); + } + } + @VisibleForTesting void setAllowedUid(int uid) { mAllowedUid = uid; @@ -243,8 +329,7 @@ public class PersistentDataBlockService extends SystemService { formatPartitionLocked(true); } } - - setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0"); + setOemUnlockEnabledProperty(enabled); } private void enforceOemUnlockReadPermission() { @@ -263,9 +348,18 @@ public class PersistentDataBlockService extends SystemService { "Can't modify OEM unlock state"); } + private void enforceConfigureFrpPermission() { + if (mFrpEnforced && mContext.checkCallingOrSelfPermission( + Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException(("Can't configure Factory Reset Protection. Requires " + + "CONFIGURE_FACTORY_RESET_PROTECTION")); + } + } + private void enforceUid(int callingUid) { - if (callingUid != mAllowedUid) { - throw new SecurityException("uid " + callingUid + " not allowed to access PST"); + if (callingUid != mAllowedUid && callingUid != UserHandle.AID_ROOT) { + throw new SecurityException("uid " + callingUid + " not allowed to access PDB"); } } @@ -315,7 +409,9 @@ public class PersistentDataBlockService extends SystemService { @VisibleForTesting int getMaximumFrpDataSize() { - return (int) (getTestHarnessModeDataOffset() - DIGEST_SIZE_BYTES - HEADER_SIZE); + long frpSecretSize = mFrpEnforced ? FRP_SECRET_MAGIC.length + FRP_SECRET_SIZE : 0; + return (int) (getTestHarnessModeDataOffset() - DIGEST_SIZE_BYTES - HEADER_SIZE + - frpSecretSize); } @VisibleForTesting @@ -324,6 +420,16 @@ public class PersistentDataBlockService extends SystemService { } @VisibleForTesting + long getFrpSecretMagicOffset() { + return getFrpSecretDataOffset() - FRP_SECRET_MAGIC.length; + } + + @VisibleForTesting + long getFrpSecretDataOffset() { + return getTestHarnessModeDataOffset() - FRP_SECRET_SIZE; + } + + @VisibleForTesting long getTestHarnessModeDataOffset() { return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE; } @@ -349,6 +455,11 @@ public class PersistentDataBlockService extends SystemService { } private FileChannel getBlockOutputChannel() throws IOException { + enforceFactoryResetProtectionInactive(); + return getBlockOutputChannelIgnoringFrp(); + } + + private FileChannel getBlockOutputChannelIgnoringFrp() throws FileNotFoundException { return new RandomAccessFile(mDataBlockFile, "rw").getChannel(); } @@ -416,7 +527,7 @@ public class PersistentDataBlockService extends SystemService { @VisibleForTesting void formatPartitionLocked(boolean setOemUnlockEnabled) { - try (FileChannel channel = getBlockOutputChannel()) { + try (FileChannel channel = getBlockOutputChannelIgnoringFrp()) { // Format the data selectively. // // 1. write header, set length = 0 @@ -431,12 +542,18 @@ public class PersistentDataBlockService extends SystemService { // 2. corrupt the legacy FRP data explicitly int payload_size = (int) getBlockDeviceSize() - header_size; - buf = ByteBuffer.allocate(payload_size - - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1); + if (mFrpEnforced) { + buf = ByteBuffer.allocate(payload_size - TEST_MODE_RESERVED_SIZE + - FRP_SECRET_MAGIC.length - FRP_SECRET_SIZE - FRP_CREDENTIAL_RESERVED_SIZE + - 1); + } else { + buf = ByteBuffer.allocate(payload_size - TEST_MODE_RESERVED_SIZE + - FRP_CREDENTIAL_RESERVED_SIZE - 1); + } channel.write(buf); channel.force(true); - // 3. skip the test mode data and leave it unformat + // 3. skip the test mode data and leave it unformatted. // This is for a feature that enables testing. channel.position(channel.position() + TEST_MODE_RESERVED_SIZE); @@ -451,6 +568,11 @@ public class PersistentDataBlockService extends SystemService { buf.flip(); channel.write(buf); channel.force(true); + + // 6. Write the default FRP secret (all zeros). + if (mFrpEnforced) { + writeFrpMagicAndDefaultSecret(); + } } catch (IOException e) { Slog.e(TAG, "failed to format block", e); return; @@ -460,10 +582,198 @@ public class PersistentDataBlockService extends SystemService { computeAndWriteDigestLocked(); } - private void doSetOemUnlockEnabledLocked(boolean enabled) { + /** + * Try to deactivate FRP by presenting an FRP secret from the data partition, or the default + * secret if the secret(s) on the data partition are not present or don't work. + */ + @VisibleForTesting + boolean automaticallyDeactivateFrpIfPossible() { + synchronized (mLock) { + if (deactivateFrpWithFileSecret(mFrpSecretFile)) { + return true; + } - try (FileChannel channel = getBlockOutputChannel()) { + Slog.w(TAG, "Failed to deactivate with primary secret file, trying backup."); + if (deactivateFrpWithFileSecret(mFrpSecretTmpFile)) { + // The backup file has the FRP secret, make it the primary file. + moveFrpTempFileToPrimary(); + return true; + } + + Slog.w(TAG, "Failed to deactivate with backup secret file, trying default secret."); + if (deactivateFrp(new byte[FRP_SECRET_SIZE])) { + return true; + } + + // We could not deactivate FRP. It's possible that we have hit an obscure corner case, + // a device that once ran a version of Android that set the FRP magic and a secret, + // then downgraded to a version that did not know about FRP, wiping the FRP secrets + // files, then upgraded to a version (the current one) that does know about FRP, + // potentially leaving the user unable to deactivate FRP because all copies of the + // secret are gone. + // + // To handle this case, we check to see if we have recently upgraded from a pre-V + // version. If so, we deactivate FRP and set the secret to the default value. + if (isUpgradingFromPreVRelease()) { + Slog.w(TAG, "Upgrading from Android 14 or lower, defaulting FRP secret"); + writeFrpMagicAndDefaultSecret(); + mFrpActive = false; + return true; + } + + Slog.e(TAG, "Did not find valid FRP secret, FRP remains active."); + return false; + } + } + + private boolean deactivateFrpWithFileSecret(String frpSecretFile) { + try { + return deactivateFrp(Files.readAllBytes(Paths.get(frpSecretFile))); + } catch (IOException e) { + Slog.w(TAG, "Failed to read FRP secret file: " + frpSecretFile + " " + + e.getClass().getSimpleName()); + return false; + } + } + + private void moveFrpTempFileToPrimary() { + try { + Files.move(Paths.get(mFrpSecretTmpFile), Paths.get(mFrpSecretFile), REPLACE_EXISTING); + } catch (IOException e) { + Slog.e(TAG, "Error moving FRP backup file to primary (ignored)", e); + } + } + + @VisibleForTesting + boolean isFrpActive() { + waitForInitDoneSignal(); + synchronized (mLock) { + return mFrpActive; + } + } + + /** + * Write the provided secret to the FRP secret file in /data and to the /persist partition. + * + * Writing is a three-step process, to ensure that we can recover from a crash at any point. + */ + private boolean updateFrpSecret(byte[] secret) { + // 1. Write the new secret to a temporary file, and sync the write. + try { + Files.write( + Paths.get(mFrpSecretTmpFile), secret, WRITE, CREATE, TRUNCATE_EXISTING, SYNC); + } catch (IOException e) { + Slog.e(TAG, "Failed to write FRP secret file", e); + return false; + } + + // 2. Write the new secret to /persist, and sync the write. + if (!mInternalService.writeDataBuffer(getFrpSecretDataOffset(), ByteBuffer.wrap(secret))) { + return false; + } + + // 3. Move the temporary file to the primary file location. Syncing doesn't matter + // here. In the event this update doesn't complete it will get done by + // #automaticallyDeactivateFrpIfPossible() during the next boot. + moveFrpTempFileToPrimary(); + return true; + } + + /** + * Only for testing, activate FRP. + */ + @VisibleForTesting + void activateFrp() { + synchronized (mLock) { + mFrpActive = true; + } + } + + private boolean hasFrpSecretMagic() { + final byte[] frpMagic = + readDataBlock(getFrpSecretMagicOffset(), FRP_SECRET_MAGIC.length); + if (frpMagic == null) { + // Transient read error on the partition? + Slog.e(TAG, "Failed to read FRP magic region."); + return false; + } + return Arrays.equals(frpMagic, FRP_SECRET_MAGIC); + } + private byte[] getFrpSecret() { + return readDataBlock(getFrpSecretDataOffset(), FRP_SECRET_SIZE); + } + + private boolean deactivateFrp(byte[] secret) { + if (secret == null || secret.length != FRP_SECRET_SIZE) { + Slog.w(TAG, "Attempted to deactivate FRP with a null or incorrectly-sized secret"); + return false; + } + + synchronized (mLock) { + if (!hasFrpSecretMagic()) { + Slog.i(TAG, "No FRP secret magic, system must have been upgraded."); + writeFrpMagicAndDefaultSecret(); + } + } + + final byte[] partitionSecret = getFrpSecret(); + if (partitionSecret == null || partitionSecret.length != FRP_SECRET_SIZE) { + Slog.e(TAG, "Failed to read FRP secret from persistent data partition"); + return false; + } + + // MessageDigest.isEqual is constant-time, to protect secret deduction by timing attack. + if (MessageDigest.isEqual(secret, partitionSecret)) { + mFrpActive = false; + Slog.i(TAG, "FRP secret matched, FRP deactivated."); + return true; + } else { + Slog.e(TAG, + "FRP deactivation failed with secret " + HexFormat.of().formatHex(secret)); + return false; + } + } + + private void writeFrpMagicAndDefaultSecret() { + try (FileChannel channel = getBlockOutputChannelIgnoringFrp()) { + synchronized (mLock) { + // Write secret first in case we crash between the writes, causing the first write + // to be synced but the second to be lost. + Slog.i(TAG, "Writing default FRP secret"); + channel.position(getFrpSecretDataOffset()); + channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE)); + channel.force(true); + + Slog.i(TAG, "Writing FRP secret magic"); + channel.position(getFrpSecretMagicOffset()); + channel.write(ByteBuffer.wrap(FRP_SECRET_MAGIC)); + channel.force(true); + + mFrpActive = false; + } + } catch (IOException e) { + Slog.e(TAG, "Failed to write FRP magic and default secret", e); + } + } + + @VisibleForTesting + byte[] readDataBlock(long offset, int length) { + try (DataInputStream inputStream = + new DataInputStream(new FileInputStream(new File(mDataBlockFile)))) { + synchronized (mLock) { + inputStream.skip(offset); + byte[] bytes = new byte[length]; + inputStream.readFully(bytes); + return bytes; + } + } catch (IOException e) { + throw new IllegalStateException("persistent partition not readable", e); + } + } + + private void doSetOemUnlockEnabledLocked(boolean enabled) { + try (FileChannel channel = getBlockOutputChannel()) { channel.position(getBlockDeviceSize() - 1); ByteBuffer data = ByteBuffer.allocate(1); @@ -475,7 +785,7 @@ public class PersistentDataBlockService extends SystemService { Slog.e(TAG, "unable to access persistent partition", e); return; } finally { - setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0"); + setOemUnlockEnabledProperty(enabled); } } @@ -507,8 +817,10 @@ public class PersistentDataBlockService extends SystemService { } private long doGetMaximumDataBlockSize() { - long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES - - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1; + final long frpSecretSize = + mFrpEnforced ? (FRP_SECRET_MAGIC.length + FRP_SECRET_SIZE) : 0; + final long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES + - TEST_MODE_RESERVED_SIZE - frpSecretSize - FRP_CREDENTIAL_RESERVED_SIZE - 1; return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE; } @@ -526,6 +838,140 @@ public class PersistentDataBlockService extends SystemService { } private final IBinder mService = new IPersistentDataBlockService.Stub() { + private int printFrpStatus(PrintWriter pw, boolean printSecrets) { + enforceUid(Binder.getCallingUid()); + + pw.println("FRP state"); + pw.println("========="); + pw.println("Enforcement enabled: " + mFrpEnforced); + pw.println("FRP state: " + mFrpActive); + printFrpDataFilesContents(pw, printSecrets); + printFrpSecret(pw, printSecrets); + pw.println("OEM unlock state: " + getOemUnlockEnabled()); + pw.println("Bootloader lock state: " + getFlashLockState()); + pw.println("Verified boot state: " + getVerifiedBootState()); + pw.println("Has FRP credential handle: " + hasFrpCredentialHandle()); + pw.println("FRP challenge block size: " + getDataBlockSize()); + return 1; + } + + private void printFrpSecret(PrintWriter pw, boolean printSecret) { + if (hasFrpSecretMagic()) { + if (printSecret) { + pw.println("FRP secret in PDB: " + HexFormat.of().formatHex( + readDataBlock(getFrpSecretDataOffset(), FRP_SECRET_SIZE))); + } else { + pw.println("FRP secret present but omitted."); + } + } else { + pw.println("FRP magic not found"); + } + } + + private void printFrpDataFilesContents(PrintWriter pw, boolean printSecrets) { + printFrpDataFileContents(pw, mFrpSecretFile, printSecrets); + printFrpDataFileContents(pw, mFrpSecretTmpFile, printSecrets); + } + + private void printFrpDataFileContents( + PrintWriter pw, String frpSecretFile, boolean printSecret) { + if (Files.exists(Paths.get(frpSecretFile))) { + if (printSecret) { + try { + pw.println("FRP secret in " + frpSecretFile + ": " + HexFormat.of() + .formatHex(Files.readAllBytes(Paths.get(mFrpSecretFile)))); + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + frpSecretFile, e); + } + } else { + pw.println( + "FRP secret file " + frpSecretFile + " exists, contents omitted."); + } + } + } + + @Override + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, + @Nullable FileDescriptor err, + @NonNull String[] args, @Nullable ShellCallback callback, + @NonNull ResultReceiver resultReceiver) throws RemoteException { + if (!mFrpEnforced) { + super.onShellCommand(in, out, err, args, callback, resultReceiver); + return; + } + new ShellCommand(){ + @Override + public int onCommand(final String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + + final PrintWriter pw = getOutPrintWriter(); + return switch (cmd) { + case "status" -> printFrpStatus(pw, /* printSecrets */ !mFrpActive); + case "activate" -> { + activateFrp(); + yield printFrpStatus(pw, /* printSecrets */ !mFrpActive); + } + + case "deactivate" -> { + byte[] secret = hashSecretString(getNextArg()); + pw.println("Attempting to deactivate with: " + HexFormat.of().formatHex( + secret)); + pw.println("Deactivation " + + (deactivateFrp(secret) ? "succeeded" : "failed")); + yield printFrpStatus(pw, /* printSecrets */ !mFrpActive); + } + + case "auto_deactivate" -> { + boolean result = automaticallyDeactivateFrpIfPossible(); + pw.println( + "Automatic deactivation " + (result ? "succeeded" : "failed")); + yield printFrpStatus(pw, /* printSecrets */ !mFrpActive); + } + + case "set_secret" -> { + byte[] secret = new byte[FRP_SECRET_SIZE]; + String secretString = getNextArg(); + if (!secretString.equals("default")) { + secret = hashSecretString(secretString); + } + pw.println("Setting FRP secret to: " + HexFormat.of() + .formatHex(secret) + " length: " + secret.length); + setFactoryResetProtectionSecret(secret); + yield printFrpStatus(pw, /* printSecrets */ !mFrpActive); + } + + default -> handleDefaultCommands(cmd); + }; + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println("Commands"); + pw.println("status: Print the FRP state and associated information."); + pw.println("activate: Put FRP into \"active\" mode."); + pw.println("deactivate <secret>: Deactivate with a hash of 'secret'."); + pw.println("auto_deactivate: Deactivate with the stored secret or the default"); + pw.println("set_secret <secret>: Set the stored secret to a hash of `secret`"); + } + + private static byte[] hashSecretString(String secretInput) { + try { + // SHA-256 produces 32-byte outputs, same as the FRP secret size, so it's + // a convenient way to "normalize" the length of whatever the user provided. + // Also, hashing makes it difficult for an attacker to set the secret to a + // known value that was randomly generated. + MessageDigest md = MessageDigest.getInstance("SHA-256"); + return md.digest(secretInput.getBytes()); + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Can't happen", e); + return new byte[FRP_SECRET_SIZE]; + } + } + }.exec(this, in, out, err, args, callback, resultReceiver); + } /** * Write the data to the persistent data block. @@ -545,7 +991,7 @@ public class PersistentDataBlockService extends SystemService { } ByteBuffer headerAndData = ByteBuffer.allocate( - data.length + HEADER_SIZE + DIGEST_SIZE_BYTES); + data.length + HEADER_SIZE + DIGEST_SIZE_BYTES); headerAndData.put(new byte[DIGEST_SIZE_BYTES]); headerAndData.putInt(PARTITION_TYPE_MARKER); headerAndData.putInt(data.length); @@ -619,6 +1065,7 @@ public class PersistentDataBlockService extends SystemService { @Override public void wipe() { + enforceFactoryResetProtectionInactive(); enforceOemUnlockWritePermission(); synchronized (mLock) { @@ -626,7 +1073,7 @@ public class PersistentDataBlockService extends SystemService { if (mIsFileBacked) { try { Files.write(Paths.get(mDataBlockFile), new byte[MAX_DATA_BLOCK_SIZE], - StandardOpenOption.TRUNCATE_EXISTING); + TRUNCATE_EXISTING); ret = 0; } catch (IOException e) { ret = -1; @@ -685,6 +1132,10 @@ public class PersistentDataBlockService extends SystemService { } } + private static String getVerifiedBootState() { + return SystemProperties.get(VERIFIED_BOOT_STATE); + } + @Override public int getDataBlockSize() { enforcePersistentDataBlockAccess(); @@ -716,6 +1167,18 @@ public class PersistentDataBlockService extends SystemService { } } + private void enforceConfigureFrpPermissionOrPersistentDataBlockAccess() { + if (!mFrpEnforced) { + enforcePersistentDataBlockAccess(); + } else { + if (mContext.checkCallingOrSelfPermission( + Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION) + == PackageManager.PERMISSION_DENIED) { + enforcePersistentDataBlockAccess(); + } + } + } + @Override public long getMaximumDataBlockSize() { enforceUid(Binder.getCallingUid()); @@ -724,7 +1187,7 @@ public class PersistentDataBlockService extends SystemService { @Override public boolean hasFrpCredentialHandle() { - enforcePersistentDataBlockAccess(); + enforceConfigureFrpPermissionOrPersistentDataBlockAccess(); try { return mInternalService.getFrpCredentialHandle() != null; } catch (IllegalStateException e) { @@ -751,9 +1214,51 @@ public class PersistentDataBlockService extends SystemService { synchronized (mLock) { pw.println("mIsWritable: " + mIsWritable); } + printFrpStatus(pw, /* printSecrets */ false); + } + + @Override + public boolean isFactoryResetProtectionActive() { + return isFrpActive(); + } + + @Override + public boolean deactivateFactoryResetProtection(byte[] secret) { + enforceConfigureFrpPermission(); + return deactivateFrp(secret); + } + + @Override + public boolean setFactoryResetProtectionSecret(byte[] secret) { + enforceUid(Binder.getCallingUid()); + if (secret == null || secret.length != FRP_SECRET_SIZE) { + throw new IllegalArgumentException( + "Invalid FRP secret: " + HexFormat.of().formatHex(secret)); + } + enforceFactoryResetProtectionInactive(); + return updateFrpSecret(secret); } }; + private void enforceFactoryResetProtectionInactive() { + if (mFrpEnforced && isFrpActive()) { + throw new SecurityException("FRP is active"); + } + } + + @VisibleForTesting + boolean isUpgradingFromPreVRelease() { + PackageManagerInternal packageManagerInternal = + LocalServices.getService(PackageManagerInternal.class); + if (packageManagerInternal == null) { + Slog.e(TAG, "Unable to retrieve PackageManagerInternal"); + return false; + } + + return packageManagerInternal + .isUpgradingFromLowerThan(Build.VERSION_CODES.VANILLA_ICE_CREAM); + } + private InternalService mInternalService = new InternalService(); private class InternalService implements PersistentDataBlockManagerInternal { @@ -792,6 +1297,14 @@ public class PersistentDataBlockService extends SystemService { return mAllowedUid; } + @Override + public boolean deactivateFactoryResetProtectionWithoutSecret() { + synchronized (mLock) { + mFrpActive = false; + } + return true; + } + private void writeInternal(byte[] data, long offset, int dataLength) { checkArgument(data == null || data.length > 0, "data must be null or non-empty"); checkArgument( @@ -808,10 +1321,10 @@ public class PersistentDataBlockService extends SystemService { writeDataBuffer(offset, dataBuffer); } - private void writeDataBuffer(long offset, ByteBuffer dataBuffer) { + private boolean writeDataBuffer(long offset, ByteBuffer dataBuffer) { synchronized (mLock) { if (!mIsWritable) { - return; + return false; } try (FileChannel channel = getBlockOutputChannel()) { channel.position(offset); @@ -819,10 +1332,10 @@ public class PersistentDataBlockService extends SystemService { channel.force(true); } catch (IOException e) { Slog.e(TAG, "unable to access persistent partition", e); - return; + return false; } - computeAndWriteDigestLocked(); + return computeAndWriteDigestLocked(); } } @@ -864,5 +1377,5 @@ public class PersistentDataBlockService extends SystemService { computeAndWriteDigestLocked(); } } - }; + } } diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 3468081088a3..524bad58ce07 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -24,6 +24,7 @@ import android.annotation.RequiresPermission; import android.app.Flags; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; +import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IBackgroundInstallControlService; @@ -271,7 +272,7 @@ public class BackgroundInstallControlService extends SystemService { if (mPermissionManager.checkPermission( installerPackageName, android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId) != PERMISSION_GRANTED) { return; @@ -479,7 +480,7 @@ public class BackgroundInstallControlService extends SystemService { return mPermissionManager.checkPermission( pkgName, android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId) == PERMISSION_GRANTED; } diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index ac89feccef7e..9afdde53643c 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -69,6 +69,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.admin.DevicePolicyManagerInternal; +import android.companion.virtual.VirtualDeviceManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -4615,7 +4616,8 @@ public class ComputerEngine implements Computer { for (int i=0; i<permissions.length; i++) { final String permission = permissions[i]; if (mPermissionManager.checkPermission(ps.getPackageName(), permission, - Context.DEVICE_ID_DEFAULT, userId) == PERMISSION_GRANTED) { + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId) + == PERMISSION_GRANTED) { tmp[i] = true; numMatch++; } else { diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java index 2a00a442542d..104e8bee50c8 100644 --- a/services/core/java/com/android/server/pm/DumpHelper.java +++ b/services/core/java/com/android/server/pm/DumpHelper.java @@ -22,8 +22,8 @@ import static com.android.server.pm.KnownPackages.LAST_KNOWN_PACKAGE; import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo; import android.annotation.NonNull; +import android.companion.virtual.VirtualDeviceManager; import android.content.ComponentName; -import android.content.Context; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.os.Binder; @@ -162,7 +162,7 @@ final class DumpHelper { PackageManager.VERSION_CODE_HIGHEST); pw.println(mPermissionManager.checkPermission( - pkg, perm, Context.DEVICE_ID_DEFAULT, user)); + pkg, perm, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, user)); return; } else if ("l".equals(cmd) || "libraries".equals(cmd)) { dumpState.setDump(DumpState.DUMP_LIBS); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index afd4fb17dff5..dadafd7f9438 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -65,6 +65,7 @@ import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.backup.IBackupManager; import android.app.role.RoleManager; +import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; @@ -2994,8 +2995,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService // NOTE: Can't remove due to unsupported app usage public int checkPermission(String permName, String pkgName, int userId) { - return mPermissionManager.checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, - userId); + return mPermissionManager.checkPermission(pkgName, permName, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); } public String getSdkSandboxPackageName() { diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index a9e5a5434189..1c70af0a56ea 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -594,6 +594,7 @@ public class PackageInfoUtils { } ai.applicationInfo = applicationInfo; ai.requiredDisplayCategory = a.getRequiredDisplayCategory(); + ai.requireContentUriPermissionFromCaller = a.getRequireContentUriPermissionFromCaller(); ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts()); assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId); return ai; 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 40f226435194..f1dca77fcc7a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -217,7 +217,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override @PackageManager.PermissionResult - public int checkPermission(String packageName, String permissionName, int deviceId, + public int checkPermission(String packageName, String permissionName, String persistentDeviceId, @UserIdInt int userId) { // Not using Objects.requireNonNull() here for compatibility reasons. if (packageName == null || permissionName == null) { @@ -231,10 +231,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (checkPermissionDelegate == null) { return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName, - deviceId, userId); + persistentDeviceId, userId); } return checkPermissionDelegate.checkPermission(packageName, permissionName, - deviceId, userId, mPermissionManagerServiceImpl::checkPermission); + persistentDeviceId, userId, mPermissionManagerServiceImpl::checkPermission); } @Override @@ -527,17 +527,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public int getPermissionFlags(String packageName, String permissionName, int deviceId, - int userId) { + public int getPermissionFlags(String packageName, String permissionName, + String persistentDeviceId, int userId) { return mPermissionManagerServiceImpl - .getPermissionFlags(packageName, permissionName, deviceId, userId); + .getPermissionFlags(packageName, permissionName, persistentDeviceId, userId); } @Override public void updatePermissionFlags(String packageName, String permissionName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int userId) { mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask, - flagValues, checkAdjustPolicyFlagPermission, deviceId, userId); + flagValues, checkAdjustPolicyFlagPermission, persistentDeviceId, userId); } @Override @@ -577,17 +578,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void grantRuntimePermission(String packageName, String permissionName, int deviceId, - int userId) { + public void grantRuntimePermission(String packageName, String permissionName, + String persistentDeviceId, int userId) { mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, - deviceId, userId); + persistentDeviceId, userId); } @Override - public void revokeRuntimePermission(String packageName, String permissionName, int deviceId, - int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permissionName, + String persistentDeviceId, int userId, String reason) { mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName, - deviceId, userId, reason); + persistentDeviceId, userId, reason); } @Override @@ -620,9 +621,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal { @Override public int checkPermission(@NonNull String packageName, @NonNull String permissionName, - int deviceId, @UserIdInt int userId) { + @NonNull String persistentDeviceId, @UserIdInt int userId) { return PermissionManagerService.this.checkPermission(packageName, permissionName, - deviceId, userId); + persistentDeviceId, userId); } @Override @@ -888,7 +889,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @param packageName the name of the package 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 userId the user ID * @param superImpl the original implementation that can be delegated to * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has @@ -897,8 +898,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @see android.content.pm.PackageManager#checkPermission(String, String) */ int checkPermission(@NonNull String packageName, @NonNull String permissionName, - int deviceId, @UserIdInt int userId, - @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl); + String persistentDeviceId, @UserIdInt int userId, + @NonNull QuadFunction<String, String, String, Integer, Integer> superImpl); /** * Check whether the given UID has been granted the specified permission. @@ -940,18 +941,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public int checkPermission(@NonNull String packageName, @NonNull String permissionName, - int deviceId, int userId, - @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) { + String persistentDeviceId, int userId, + @NonNull QuadFunction<String, String, String, Integer, Integer> superImpl) { if (mDelegatedPackageName.equals(packageName) && isDelegatedPermission(permissionName)) { final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply("com.android.shell", permissionName, deviceId, userId); + return superImpl.apply("com.android.shell", permissionName, persistentDeviceId, + userId); } finally { Binder.restoreCallingIdentity(identity); } } - return superImpl.apply(packageName, permissionName, deviceId, userId); + return superImpl.apply(packageName, permissionName, persistentDeviceId, userId); } @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 6a5736269e51..9afd36f8f0de 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,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) { + public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + int userId) { final int callingUid = Binder.getCallingUid(); return getPermissionFlagsInternal(packageName, permName, callingUid, userId); } @@ -725,7 +726,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int userId) { final int callingUid = Binder.getCallingUid(); boolean overridePolicy = false; @@ -910,11 +912,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } private int checkPermission(String pkgName, String permName, int userId) { - return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId); + return checkPermission(pkgName, permName, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, + userId); } @Override - public int checkPermission(String pkgName, String permName, int deviceId, int userId) { + public int checkPermission(String pkgName, String permName, String persistentDeviceId, + int userId) { if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; } @@ -1304,8 +1308,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public void grantRuntimePermission(String packageName, String permName, int deviceId, - int userId) { + public void grantRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) @@ -1478,12 +1482,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public void revokeRuntimePermission(String packageName, String permName, int deviceId, - int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId, String reason) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = - checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId) - == PackageManager.PERMISSION_GRANTED; + checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, + Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED; revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId, reason, mDefaultPermissionCallback); @@ -2070,8 +2074,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt continue; } boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(), - permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & ( - FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0; + permInfo.name, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId) + & (FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0; if (isSystemOrPolicyFixed) { continue; } @@ -2238,7 +2242,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt final int permissionState = checkPermission(packageName, permName, userId); final int flags = getPermissionFlags(packageName, permName, - Context.DEVICE_ID_DEFAULT, userId); + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId); final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_GRANTED_BY_DEFAULT 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 2d824aa1ba13..b12d8acc11b4 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -140,11 +140,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 deviceId The device for which to get the flags + * @param persistentDeviceId 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, int deviceId, + int getPermissionFlags(String packageName, String permName, String persistentDeviceId, @UserIdInt int userId); /** @@ -155,11 +155,12 @@ 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 deviceId The device for which to update the permission flags + * @param persistentDeviceId 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, int deviceId, @UserIdInt int userId); + boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + @UserIdInt int userId); /** * Update the permission flags for all packages and runtime permissions of a user in order @@ -293,17 +294,17 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param packageName the package to which to grant the permission * @param permName the permission name to grant - * @param deviceId the device for which to grant the permission + * @param persistentDeviceId the device for which to grant the permission * @param userId the user for which to grant the permission * - * @see #revokeRuntimePermission(String, String, int, int, String) + * @see #revokeRuntimePermission(String, String, String, int, String) */ - void grantRuntimePermission(String packageName, String permName, int deviceId, + void grantRuntimePermission(String packageName, String permName, String persistentDeviceId, @UserIdInt int userId); /** * Revoke a runtime permission that was previously granted by - * {@link #grantRuntimePermission(String, String, android.os.UserHandle)}. The permission must + * {@link #grantRuntimePermission(String, String, String, int)}. The permission must * have been requested by and granted to the application. If the application is not allowed to * hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or * permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown. @@ -314,13 +315,13 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param packageName the package from which to revoke the permission * @param permName the permission name to revoke - * @param deviceId the device for which to revoke the permission + * @param persistentDeviceId 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, int, int) + * @see #grantRuntimePermission(String, String, String, int) */ - void revokeRuntimePermission(String packageName, String permName, int deviceId, + void revokeRuntimePermission(String packageName, String permName, String persistentDeviceId, @UserIdInt int userId, String reason); /** @@ -387,11 +388,12 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * * @param pkgName package name * @param permName permission name - * @param deviceId device ID + * @param persistentDeviceId persistent device ID * @param userId user ID * @return permission result {@link PackageManager.PermissionResult} */ - int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId); + int checkPermission(String pkgName, String permName, String persistentDeviceId, + @UserIdInt int userId); /** * Check whether a permission is granted or not to an UID. diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 98adeb66388e..132cdcee8f8e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -46,14 +46,14 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter * * @param packageName the name of the package you are checking against * @param permissionName the name of the permission you are checking for - * @param deviceId the device ID + * @param persistentDeviceId the persistent device ID to check permission for * @param userId the user ID * @return {@code PERMISSION_GRANTED} if the permission is granted, or {@code PERMISSION_DENIED} * otherwise */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - int checkPermission(@NonNull String packageName, @NonNull String permissionName, int deviceId, - @UserIdInt int userId); + int checkPermission(@NonNull String packageName, @NonNull String permissionName, + @NonNull String persistentDeviceId, @UserIdInt int userId); /** * Check whether a particular UID has been granted a particular permission. 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 dacb8c6890a0..835ddcbfc2ba 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java @@ -120,21 +120,24 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) { + public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + int userId) { Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = " - + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")"); - return mService.getPermissionFlags(packageName, permName, deviceId, userId); + + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + + ")"); + return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId); } @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int userId) { Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = " + permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues + ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission - + ", deviceId = " + deviceId + ", userId = " + userId + ")"); + + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")"); mService.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, deviceId, userId); + checkAdjustPolicyFlagPermission, persistentDeviceId, userId); } @Override @@ -182,20 +185,21 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public void grantRuntimePermission(String packageName, String permName, int deviceId, - int userId) { + public void grantRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId) { Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = " - + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")"); - mService.grantRuntimePermission(packageName, permName, deviceId, userId); + + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + + ")"); + mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId); } @Override - public void revokeRuntimePermission(String packageName, String permName, int deviceId, - int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId, String reason) { Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = " - + permName + ", deviceId = " + deviceId + ", userId = " + userId + + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ", reason = " + reason + ")"); - mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); + mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId, reason); } @Override @@ -230,10 +234,11 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag } @Override - public int checkPermission(String pkgName, String permName, int deviceId, int userId) { + public int checkPermission(String pkgName, String permName, String persistentDeviceId, + int userId) { Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName - + ", deviceId = " + deviceId + ", userId = " + userId + ")"); - return mService.checkPermission(pkgName, permName, deviceId, userId); + + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")"); + return mService.checkPermission(pkgName, permName, persistentDeviceId, 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 35d165b9b54a..66a6f3cf39dc 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java @@ -153,10 +153,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public int getPermissionFlags(String packageName, String permName, int deviceId, + public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, @UserIdInt int userId) { - int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId); - int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId); + int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, + persistentDeviceId, userId); + int newVal = mNewImplementation.getPermissionFlags(packageName, permName, + persistentDeviceId, userId); if (!Objects.equals(oldVal, newVal)) { signalImplDifference("getPermissionFlags"); @@ -166,12 +168,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, @UserIdInt int userId) { mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, deviceId, userId); + checkAdjustPolicyFlagPermission, persistentDeviceId, userId); mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, deviceId, userId); + checkAdjustPolicyFlagPermission, persistentDeviceId, userId); } @Override @@ -236,17 +238,21 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public void grantRuntimePermission(String packageName, String permName, int deviceId, - @UserIdInt int userId) { - mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId); - mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId); + public void grantRuntimePermission(String packageName, String permName, + String persistentDeviceId, @UserIdInt int userId) { + mOldImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId, + userId); + mNewImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId, + userId); } @Override - public void revokeRuntimePermission(String packageName, String permName, int deviceId, - @UserIdInt int userId, String reason) { - mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); - mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); + 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); } @Override @@ -296,9 +302,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer } @Override - public int checkPermission(String pkgName, String permName, int deviceId, int userId) { - int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId); - int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId); + 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); if (!Objects.equals(oldVal, newVal)) { signalImplDifference("checkPermission"); 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 cbeede0f425c..f21993ca97cf 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java @@ -158,10 +158,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) { + public int getPermissionFlags(String packageName, String permName, String persistentDeviceId, + int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags"); try { - return mService.getPermissionFlags(packageName, permName, deviceId, userId); + return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } @@ -169,12 +170,13 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId, + int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags"); try { mService.updatePermissionFlags(packageName, permName, flagMask, flagValues, - checkAdjustPolicyFlagPermission, deviceId, userId); + checkAdjustPolicyFlagPermission, persistentDeviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } @@ -253,24 +255,25 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public void grantRuntimePermission(String packageName, String permName, int deviceId, - int userId) { + public void grantRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission"); try { - mService.grantRuntimePermission(packageName, permName, deviceId, userId); + mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } } @Override - public void revokeRuntimePermission(String packageName, String permName, int deviceId, - int userId, String reason) { + public void revokeRuntimePermission(String packageName, String permName, + String persistentDeviceId, int userId, String reason) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission"); try { - mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason); + mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId, + reason); } finally { Trace.traceEnd(TRACE_TAG); } @@ -324,10 +327,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag } @Override - public int checkPermission(String pkgName, String permName, int deviceId, int userId) { + public int checkPermission(String pkgName, String permName, String persistentDeviceId, + int userId) { Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission"); try { - return mService.checkPermission(pkgName, permName, deviceId, userId); + return mService.checkPermission(pkgName, permName, persistentDeviceId, userId); } finally { Trace.traceEnd(TRACE_TAG); } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java index 03c75e018dab..195e91cf5716 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.uri; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; +import android.content.pm.ActivityInfo.RequiredContentUriPermission; import android.content.pm.ProviderInfo; import android.net.Uri; import android.os.IBinder; @@ -63,6 +64,15 @@ public interface UriGrantsManagerInternal { String targetPkg, int targetUserId); /** + * Same as {@link #checkGrantUriPermissionFromIntent(Intent, int, String, int)}, but with an + * extra parameter {@code requireContentUriPermissionFromCaller}, which is the value from {@link + * android.R.attr#requireContentUriPermissionFromCaller} attribute. + */ + NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid, + String targetPkg, int targetUserId, + @RequiredContentUriPermission int requireContentUriPermissionFromCaller); + + /** * Extend a previously calculated set of permissions grants to the given * owner. All security checks will have already been performed as part of * calculating {@link NeededUriGrants}. diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index ce2cbed0c9a9..d2f6701e313e 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -23,6 +23,10 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; +import static android.content.pm.ActivityInfo.CONTENT_URI_PERMISSION_NONE; +import static android.content.pm.ActivityInfo.CONTENT_URI_PERMISSION_READ_OR_WRITE; +import static android.content.pm.ActivityInfo.isRequiredContentUriPermissionRead; +import static android.content.pm.ActivityInfo.isRequiredContentUriPermissionWrite; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO; @@ -53,6 +57,8 @@ import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ActivityInfo.RequiredContentUriPermission; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; @@ -609,7 +615,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements /** Like checkGrantUriPermission, but takes an Intent. */ private NeededUriGrants checkGrantUriPermissionFromIntentUnlocked(int callingUid, - String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) { + String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId, + @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller) { if (DEBUG) Slog.v(TAG, "Checking URI perm to data=" + (intent != null ? intent.getData() : null) + " clip=" + (intent != null ? intent.getClipData() : null) @@ -647,6 +654,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements } if (data != null) { GrantUri grantUri = GrantUri.resolve(contentUserHint, data, mode); + if (android.security.Flags.contentUriPermissionApis()) { + enforceRequireContentUriPermissionFromCaller(requireContentUriPermissionFromCaller, + grantUri, callingUid); + } targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode, targetUid); if (targetUid > 0) { @@ -661,6 +672,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements Uri uri = clip.getItemAt(i).getUri(); if (uri != null) { GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode); + if (android.security.Flags.contentUriPermissionApis()) { + enforceRequireContentUriPermissionFromCaller( + requireContentUriPermissionFromCaller, grantUri, callingUid); + } targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode, targetUid); if (targetUid > 0) { @@ -673,7 +688,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements Intent clipIntent = clip.getItemAt(i).getIntent(); if (clipIntent != null) { NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentUnlocked( - callingUid, targetPkg, clipIntent, mode, needed, targetUserId); + callingUid, targetPkg, clipIntent, mode, needed, targetUserId, + requireContentUriPermissionFromCaller); if (newNeeded != null) { needed = newNeeded; } @@ -685,6 +701,38 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements return needed; } + private void enforceRequireContentUriPermissionFromCaller( + @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller, + GrantUri grantUri, int uid) { + // Ignore if requireContentUriPermissionFromCaller hasn't been set or the URI is a + // non-content URI. + if (requireContentUriPermissionFromCaller == null + || requireContentUriPermissionFromCaller == CONTENT_URI_PERMISSION_NONE + || !ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) { + return; + } + + final boolean readMet = !isRequiredContentUriPermissionRead( + requireContentUriPermissionFromCaller) + || checkContentUriPermissionFullUnlocked(grantUri, uid, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + + final boolean writeMet = !isRequiredContentUriPermissionWrite( + requireContentUriPermissionFromCaller) + || checkContentUriPermissionFullUnlocked(grantUri, uid, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + boolean hasPermission = + requireContentUriPermissionFromCaller == CONTENT_URI_PERMISSION_READ_OR_WRITE + ? (readMet || writeMet) : (readMet && writeMet); + + if (!hasPermission) { + throw new SecurityException("You can't launch this activity because you don't have the" + + " required " + ActivityInfo.requiredContentUriPermissionToShortString( + requireContentUriPermissionFromCaller) + " access to " + grantUri.uri); + } + } + @GuardedBy("mLock") private void readGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()"); @@ -1560,9 +1608,24 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements @Override public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid, String targetPkg, int targetUserId) { + return internalCheckGrantUriPermissionFromIntent(intent, callingUid, targetPkg, + targetUserId, /* requireContentUriPermissionFromCaller */ null); + } + + @Override + public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid, + String targetPkg, int targetUserId, int requireContentUriPermissionFromCaller) { + return internalCheckGrantUriPermissionFromIntent(intent, callingUid, targetPkg, + targetUserId, requireContentUriPermissionFromCaller); + } + + private NeededUriGrants internalCheckGrantUriPermissionFromIntent(Intent intent, + int callingUid, String targetPkg, int targetUserId, + @Nullable Integer requireContentUriPermissionFromCaller) { final int mode = (intent != null) ? intent.getFlags() : 0; return UriGrantsManagerService.this.checkGrantUriPermissionFromIntentUnlocked( - callingUid, targetPkg, intent, mode, null, targetUserId); + callingUid, targetPkg, intent, mode, null, targetUserId, + requireContentUriPermissionFromCaller); } @Override diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java index 8f8fe3cd48f5..17a9e3330375 100644 --- a/services/core/java/com/android/server/vibrator/VibratorControlService.java +++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java @@ -248,8 +248,6 @@ public final class VibratorControlService extends IVibratorControlService.Stub { IVibratorController vibratorController = mVibratorControllerHolder.getVibratorController(); if (vibratorController == null) { - Slog.d(TAG, "Unable to check if should request vibration params. " - + "There is no registered IVibrationController."); return false; } diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java index e1abae808f10..88d3daf359c2 100644 --- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java +++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java @@ -118,4 +118,62 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable } post(service -> service.provideData(data, sharedMemory, callback)); } + + /** + * Registers a data request observer with WearableSensingService. + * + * @param dataType The data type to listen to. Values are defined by the application that + * implements WearableSensingService. + * @param dataRequestCallback The observer to send data requests to. + * @param dataRequestObserverId The unique ID for the data request observer. It will be used for + * unregistering the observer. + * @param packageName The package name of the app that will receive the data requests. + * @param statusCallback The callback for status of the method call. + */ + public void registerDataRequestObserver( + int dataType, + RemoteCallback dataRequestCallback, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + if (DEBUG) { + Slog.i(TAG, "Registering data request observer."); + } + var unused = + post( + service -> + service.registerDataRequestObserver( + dataType, + dataRequestCallback, + dataRequestObserverId, + packageName, + statusCallback)); + } + + /** + * Unregisters a previously registered data request observer. + * + * @param dataType The data type the observer was registered against. + * @param dataRequestObserverId The unique ID of the observer to unregister. + * @param packageName The package name of the app that will receive requests sent to the + * observer. + * @param statusCallback The callback for status of the method call. + */ + public void unregisterDataRequestObserver( + int dataType, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + if (DEBUG) { + Slog.i(TAG, "Unregistering data request observer."); + } + var unused = + post( + service -> + service.unregisterDataRequestObserver( + dataType, + dataRequestObserverId, + packageName, + statusCallback)); + } } diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java index a8d63228775f..0e8b82f75426 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java @@ -262,4 +262,65 @@ final class WearableSensingManagerPerUserService extends mRemoteService.provideData(data, sharedMemory, callback); } } + + /** + * Handles registering a data request observer. + * + * @param dataType The data type to listen to. Values are defined by the application that + * implements WearableSensingService. + * @param dataRequestObserver The observer to register. + * @param dataRequestObserverId The unique ID for the data request observer. It will be used for + * unregistering the observer. + * @param packageName The package name of the app that will receive the data requests. + * @param statusCallback The callback for status of the method call. + */ + public void onRegisterDataRequestObserver( + int dataType, + RemoteCallback dataRequestObserver, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + synchronized (mLock) { + if (!setUpServiceIfNeeded()) { + Slog.w(TAG, "Detection service is not available at this moment."); + notifyStatusCallback( + statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + ensureRemoteServiceInitiated(); + mRemoteService.registerDataRequestObserver( + dataType, + dataRequestObserver, + dataRequestObserverId, + packageName, + statusCallback); + } + } + + /** + * Handles unregistering a previously registered data request observer. + * + * @param dataType The data type the observer was registered against. + * @param dataRequestObserverId The unique ID of the observer to unregister. + * @param packageName The package name of the app that will receive requests sent to the + * observer. + * @param statusCallback The callback for status of the method call. + */ + public void onUnregisterDataRequestObserver( + int dataType, + int dataRequestObserverId, + String packageName, + RemoteCallback statusCallback) { + synchronized (mLock) { + if (!setUpServiceIfNeeded()) { + Slog.w(TAG, "Detection service is not available at this moment."); + notifyStatusCallback( + statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + ensureRemoteServiceInitiated(); + mRemoteService.unregisterDataRequestObserver( + dataType, dataRequestObserverId, packageName, statusCallback); + } + } } diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java index 28c8f8769e15..78952fa4590b 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java @@ -21,12 +21,18 @@ import static android.provider.DeviceConfig.NAMESPACE_WEARABLE_SENSING; import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.app.BroadcastOptions; +import android.app.ComponentOptions; +import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEvent; import android.app.wearable.IWearableSensingManager; +import android.app.wearable.WearableSensingDataRequest; import android.app.wearable.WearableSensingManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManagerInternal; +import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteCallback; @@ -35,6 +41,8 @@ import android.os.SharedMemory; import android.os.ShellCallback; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.service.wearable.WearableSensingDataRequester; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; @@ -44,10 +52,13 @@ import com.android.server.SystemService; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; import com.android.server.pm.KnownPackages; +import com.android.server.utils.quota.MultiRateLimiter; import java.io.FileDescriptor; +import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** @@ -64,9 +75,38 @@ public class WearableSensingManagerService extends /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = true; + public static final int MAX_TEMPORARY_SERVICE_DURATION_MS = 30000; + private static final String RATE_LIMITER_PACKAGE_NAME = "android"; + private static final String RATE_LIMITER_TAG = + WearableSensingManagerService.class.getSimpleName(); + + private static final class DataRequestObserverContext { + final int mDataType; + final int mUserId; + final int mDataRequestObserverId; + @NonNull final PendingIntent mDataRequestPendingIntent; + @NonNull final RemoteCallback mDataRequestRemoteCallback; + + DataRequestObserverContext( + int dataType, + int userId, + int dataRequestObserverId, + PendingIntent dataRequestPendingIntent, + RemoteCallback dataRequestRemoteCallback) { + mDataType = dataType; + mUserId = userId; + mDataRequestObserverId = dataRequestObserverId; + mDataRequestPendingIntent = dataRequestPendingIntent; + mDataRequestRemoteCallback = dataRequestRemoteCallback; + } + } + private final Context mContext; + private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1); + private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>(); + private final MultiRateLimiter mDataRequestRateLimiter; volatile boolean mIsServiceEnabled; public WearableSensingManagerService(Context context) { @@ -78,6 +118,12 @@ public class WearableSensingManagerService extends PACKAGE_UPDATE_POLICY_REFRESH_EAGER | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER); mContext = context; + mDataRequestRateLimiter = + new MultiRateLimiter.Builder(context) + .addRateLimit( + WearableSensingDataRequest.getRateLimit(), + WearableSensingDataRequest.getRateLimitWindowSize()) + .build(); } @Override @@ -192,6 +238,96 @@ public class WearableSensingManagerService extends } } + private DataRequestObserverContext getDataRequestObserverContext( + int dataType, int userId, PendingIntent dataRequestPendingIntent) { + synchronized (mDataRequestObserverContexts) { + for (DataRequestObserverContext observerContext : mDataRequestObserverContexts) { + if (observerContext.mDataType == dataType + && observerContext.mUserId == userId + && observerContext.mDataRequestPendingIntent.equals( + dataRequestPendingIntent)) { + return observerContext; + } + } + } + return null; + } + + @NonNull + private RemoteCallback createDataRequestRemoteCallback( + PendingIntent dataRequestPendingIntent, int userId) { + return new RemoteCallback( + bundle -> { + WearableSensingDataRequest dataRequest = + bundle.getParcelable( + WearableSensingDataRequest.REQUEST_BUNDLE_KEY, + WearableSensingDataRequest.class); + if (dataRequest == null) { + Slog.e(TAG, "Received data request callback without a request."); + return; + } + RemoteCallback dataRequestStatusCallback = + bundle.getParcelable( + WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY, + RemoteCallback.class); + if (dataRequestStatusCallback == null) { + Slog.e(TAG, "Received data request callback without a status callback."); + return; + } + if (dataRequest.getDataSize() + > WearableSensingDataRequest.getMaxRequestSize()) { + Slog.w( + TAG, + TextUtils.formatSimple( + "WearableSensingDataRequest size exceeds the maximum" + + " allowed size of %s bytes. Dropping the request.", + WearableSensingDataRequest.getMaxRequestSize())); + WearableSensingManagerPerUserService.notifyStatusCallback( + dataRequestStatusCallback, + WearableSensingDataRequester.STATUS_TOO_LARGE); + return; + } + if (!mDataRequestRateLimiter.isWithinQuota( + userId, RATE_LIMITER_PACKAGE_NAME, RATE_LIMITER_TAG)) { + Slog.w(TAG, "Data request exceeded rate limit. Dropping the request."); + WearableSensingManagerPerUserService.notifyStatusCallback( + dataRequestStatusCallback, + WearableSensingDataRequester.STATUS_TOO_FREQUENT); + return; + } + Intent intent = new Intent(); + intent.putExtra( + WearableSensingManager.EXTRA_WEARABLE_SENSING_DATA_REQUEST, + dataRequest); + BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityStartMode( + ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED); + mDataRequestRateLimiter.noteEvent( + userId, RATE_LIMITER_PACKAGE_NAME, RATE_LIMITER_TAG); + final long previousCallingIdentity = Binder.clearCallingIdentity(); + try { + dataRequestPendingIntent.send( + getContext(), 0, intent, null, null, null, options.toBundle()); + WearableSensingManagerPerUserService.notifyStatusCallback( + dataRequestStatusCallback, + WearableSensingDataRequester.STATUS_SUCCESS); + Slog.i( + TAG, + TextUtils.formatSimple( + "Sending data request to %s: %s", + dataRequestPendingIntent.getCreatorPackage(), + dataRequest.toExpandedString())); + } catch (PendingIntent.CanceledException e) { + Slog.w(TAG, "Could not deliver pendingIntent: " + dataRequestPendingIntent); + WearableSensingManagerPerUserService.notifyStatusCallback( + dataRequestStatusCallback, + WearableSensingDataRequester.STATUS_OBSERVER_CANCELLED); + } finally { + Binder.restoreCallingIdentity(previousCallingIdentity); + } + }); + } + private void callPerUserServiceIfExist( Consumer<WearableSensingManagerPerUserService> serviceConsumer, RemoteCallback statusCallback) { @@ -260,8 +396,8 @@ public class WearableSensingManagerService extends Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available."); - WearableSensingManagerPerUserService.notifyStatusCallback(callback, - WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + WearableSensingManagerPerUserService.notifyStatusCallback( + callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); return; } callPerUserServiceIfExist( @@ -270,6 +406,96 @@ public class WearableSensingManagerService extends } @Override + public void registerDataRequestObserver( + int dataType, + PendingIntent dataRequestPendingIntent, + RemoteCallback statusCallback) { + Slog.i(TAG, "WearableSensingManagerInternal registerDataRequestObserver."); + Objects.requireNonNull(dataRequestPendingIntent); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG); + if (!mIsServiceEnabled) { + Slog.w(TAG, "Service not available."); + WearableSensingManagerPerUserService.notifyStatusCallback( + statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + int userId = UserHandle.getCallingUserId(); + RemoteCallback dataRequestCallback; + int dataRequestObserverId; + synchronized (mDataRequestObserverContexts) { + DataRequestObserverContext previousObserverContext = + getDataRequestObserverContext(dataType, userId, dataRequestPendingIntent); + if (previousObserverContext != null) { + Slog.i(TAG, "Received duplicate data request observer."); + dataRequestCallback = previousObserverContext.mDataRequestRemoteCallback; + dataRequestObserverId = previousObserverContext.mDataRequestObserverId; + } else { + dataRequestCallback = + createDataRequestRemoteCallback(dataRequestPendingIntent, userId); + dataRequestObserverId = mNextDataRequestObserverId.getAndIncrement(); + mDataRequestObserverContexts.add( + new DataRequestObserverContext( + dataType, + userId, + dataRequestObserverId, + dataRequestPendingIntent, + dataRequestCallback)); + } + } + callPerUserServiceIfExist( + service -> + service.onRegisterDataRequestObserver( + dataType, + dataRequestCallback, + dataRequestObserverId, + dataRequestPendingIntent.getCreatorPackage(), + statusCallback), + statusCallback); + } + + @Override + public void unregisterDataRequestObserver( + int dataType, + PendingIntent dataRequestPendingIntent, + RemoteCallback statusCallback) { + Slog.i(TAG, "WearableSensingManagerInternal unregisterDataRequestObserver."); + Objects.requireNonNull(dataRequestPendingIntent); + Objects.requireNonNull(statusCallback); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG); + if (!mIsServiceEnabled) { + Slog.w(TAG, "Service not available."); + WearableSensingManagerPerUserService.notifyStatusCallback( + statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE); + return; + } + int userId = UserHandle.getCallingUserId(); + int previousDataRequestObserverId; + String pendingIntentCreatorPackage; + synchronized (mDataRequestObserverContexts) { + DataRequestObserverContext previousObserverContext = + getDataRequestObserverContext(dataType, userId, dataRequestPendingIntent); + if (previousObserverContext == null) { + Slog.w(TAG, "Previous observer not found, cannot unregister."); + return; + } + mDataRequestObserverContexts.remove(previousObserverContext); + previousDataRequestObserverId = previousObserverContext.mDataRequestObserverId; + pendingIntentCreatorPackage = + previousObserverContext.mDataRequestPendingIntent.getCreatorPackage(); + } + callPerUserServiceIfExist( + service -> + service.onUnregisterDataRequestObserver( + dataType, + previousDataRequestObserverId, + pendingIntentCreatorPackage, + statusCallback), + statusCallback); + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { new WearableSensingShellCommand(WearableSensingManagerService.this).exec( diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 19ea9f907306..ee865d3a588a 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1610,7 +1610,7 @@ final class AccessibilityController { Slog.i(LOG_TAG, "computeChangedWindows()"); } - final List<WindowInfo> windows = new ArrayList<>(); + final List<WindowInfo> windows; final List<AccessibilityWindow> visibleWindows = new ArrayList<>(); final int topFocusedDisplayId; IBinder topFocusedWindowToken = null; @@ -1640,69 +1640,11 @@ final class AccessibilityController { } final Display display = dc.getDisplay(); display.getRealSize(mTempPoint); - final int screenWidth = mTempPoint.x; - final int screenHeight = mTempPoint.y; - - Region unaccountedSpace = mTempRegion; - unaccountedSpace.set(0, 0, screenWidth, screenHeight); mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( mDisplayId, visibleWindows); - Set<IBinder> addedWindows = mTempBinderSet; - addedWindows.clear(); - - boolean focusedWindowAdded = false; - final int visibleWindowCount = visibleWindows.size(); - - // Iterate until we figure out what is touchable for the entire screen. - for (int i = 0; i < visibleWindowCount; i++) { - final AccessibilityWindow a11yWindow = visibleWindows.get(i); - final Region regionInWindow = new Region(); - a11yWindow.getTouchableRegionInWindow(regionInWindow); - if (windowMattersToAccessibility(a11yWindow, regionInWindow, - unaccountedSpace)) { - addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); - if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { - updateUnaccountedSpace(a11yWindow, unaccountedSpace); - } - focusedWindowAdded |= a11yWindow.isFocused(); - } else if (a11yWindow.isUntouchableNavigationBar()) { - // If this widow is navigation bar without touchable region, accounting the - // region of navigation bar inset because all touch events from this region - // would be received by launcher, i.e. this region is a un-touchable one - // for the application. - unaccountedSpace.op( - getSystemBarInsetsFrame( - mService.mWindowMap.get(a11yWindow.getWindowInfo().token)), - unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - - if (unaccountedSpace.isEmpty() && focusedWindowAdded) { - break; - } - } - - // Remove child/parent references to windows that were not added. - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - WindowInfo window = windows.get(i); - if (!addedWindows.contains(window.parentToken)) { - window.parentToken = null; - } - if (window.childTokens != null) { - final int childTokenCount = window.childTokens.size(); - for (int j = childTokenCount - 1; j >= 0; j--) { - if (!addedWindows.contains(window.childTokens.get(j))) { - window.childTokens.remove(j); - } - } - // Leave the child token list if empty. - } - } - - addedWindows.clear(); + windows = buildWindowInfoListLocked(visibleWindows, mTempPoint); // Gets the top focused display Id and window token for supporting multi-display. topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId(); @@ -1718,6 +1660,74 @@ final class AccessibilityController { mInitialized = true; } + /** + * From a list of windows, decides windows to be exposed to accessibility based on touchable + * region in the screen. + */ + private List<WindowInfo> buildWindowInfoListLocked(List<AccessibilityWindow> visibleWindows, + Point screenSize) { + final List<WindowInfo> windows = new ArrayList<>(); + final Set<IBinder> addedWindows = mTempBinderSet; + addedWindows.clear(); + + boolean focusedWindowAdded = false; + + final int visibleWindowCount = visibleWindows.size(); + + Region unaccountedSpace = mTempRegion; + unaccountedSpace.set(0, 0, screenSize.x, screenSize.y); + + // Iterate until we figure out what is touchable for the entire screen. + for (int i = 0; i < visibleWindowCount; i++) { + final AccessibilityWindow a11yWindow = visibleWindows.get(i); + final Region regionInWindow = new Region(); + a11yWindow.getTouchableRegionInWindow(regionInWindow); + if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) { + addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); + if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { + updateUnaccountedSpace(a11yWindow, unaccountedSpace); + } + focusedWindowAdded |= a11yWindow.isFocused(); + } else if (a11yWindow.isUntouchableNavigationBar()) { + // If this widow is navigation bar without touchable region, accounting the + // region of navigation bar inset because all touch events from this region + // would be received by launcher, i.e. this region is a un-touchable one + // for the application. + unaccountedSpace.op( + getSystemBarInsetsFrame( + mService.mWindowMap.get(a11yWindow.getWindowInfo().token)), + unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + } + + if (unaccountedSpace.isEmpty() && focusedWindowAdded) { + break; + } + } + + // Remove child/parent references to windows that were not added. + final int windowCount = windows.size(); + for (int i = 0; i < windowCount; i++) { + WindowInfo window = windows.get(i); + if (!addedWindows.contains(window.parentToken)) { + window.parentToken = null; + } + if (window.childTokens != null) { + final int childTokenCount = window.childTokens.size(); + for (int j = childTokenCount - 1; j >= 0; j--) { + if (!addedWindows.contains(window.childTokens.get(j))) { + window.childTokens.remove(j); + } + } + // Leave the child token list if empty. + } + } + + addedWindows.clear(); + + return windows; + } + // Some windows should be excluded from unaccounted space computation, though they still // should be reported private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 85580ac6810a..d99000efeeb4 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -591,9 +591,18 @@ class ActivityStarter { // Carefully collect grants without holding lock if (activityInfo != null) { - intentGrants = supervisor.mService.mUgmInternal.checkGrantUriPermissionFromIntent( - intent, resolvedCallingUid, activityInfo.applicationInfo.packageName, - UserHandle.getUserId(activityInfo.applicationInfo.uid)); + if (android.security.Flags.contentUriPermissionApis()) { + intentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId(activityInfo.applicationInfo.uid), + activityInfo.requireContentUriPermissionFromCaller); + } else { + intentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId(activityInfo.applicationInfo.uid)); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0def5a1861ea..877336604364 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -276,6 +276,7 @@ import com.android.server.am.PendingIntentController; import com.android.server.am.PendingIntentRecord; import com.android.server.am.UserState; import com.android.server.firewall.IntentFirewall; +import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.sdksandbox.SdkSandboxManagerLocal; @@ -317,7 +318,6 @@ import java.util.Set; * {@hide} */ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { - private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender"; private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM; static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; @@ -381,6 +381,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private PowerManagerInternal mPowerManagerInternal; private UsageStatsManagerInternal mUsageStatsInternal; + GrammaticalInflectionManagerInternal mGrammaticalManagerInternal; PendingIntentController mPendingIntentController; IntentFirewall mIntentFirewall; @@ -881,6 +882,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mActivityClientController.onSystemReady(); // TODO(b/258792202) Cleanup once ASM is ready to launch ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm); + mGrammaticalManagerInternal = LocalServices.getService( + GrammaticalInflectionManagerInternal.class); } } @@ -938,13 +941,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { configuration.setLayoutDirection(configuration.locale); } - // Retrieve the grammatical gender from system property, set it into configuration which - // will get updated later if the grammatical gender raw value of current configuration is - // {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}. - if (configuration.getGrammaticalGenderRaw() == Configuration.GRAMMATICAL_GENDER_UNDEFINED) { - configuration.setGrammaticalGender(SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY, - Configuration.GRAMMATICAL_GENDER_UNDEFINED)); - } + configuration.setGrammaticalGender( + mGrammaticalManagerInternal.retrieveSystemGrammaticalGender(configuration)); synchronized (mGlobalLock) { mForceResizableActivities = forceResizable; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 25646f19c461..609ad1e76370 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -83,6 +83,7 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING; +import static com.android.systemui.shared.Flags.enableHomeDelay; import static java.lang.Integer.MAX_VALUE; @@ -1444,6 +1445,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> aInfo = info.first; homeIntent = info.second; } + if (aInfo == null || homeIntent == null) { return false; } @@ -1452,6 +1454,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } + if (enableHomeDelay() && !mService.mAmInternal.getThemeOverlayReadiness()) { + Slog.d(TAG, "ThemeHomeDelay: Home launch was deferred."); + return false; + } + // Updates the home component of the intent. homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 6d2e8cc29506..6acf1f3f84af 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.ProcessList.INVALID_ADJ; +import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission; import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.DESTROYING; import static com.android.server.wm.ActivityRecord.State.PAUSED; @@ -298,6 +299,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio */ private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER; + private boolean mCanUseSystemGrammaticalGender; + public WindowProcessController(@NonNull ActivityTaskManagerService atm, @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner, @NonNull WindowProcessListener listener) { @@ -319,6 +322,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mIsActivityConfigOverrideAllowed = false; } + mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null + && mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid, + mInfo.packageName); onConfigurationChanged(atm.getGlobalConfiguration()); mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName); } @@ -1568,6 +1574,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } + if (mCanUseSystemGrammaticalGender) { + config.setGrammaticalGender( + mAtm.mGrammaticalManagerInternal.getSystemGrammaticalGender(mUserId)); + } + if (mPauseConfigurationDispatchCount > 0) { mHasPendingConfigurationChange = true; return; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 810090adbf8e..cbbcd965f4af 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -238,7 +238,7 @@ static SpriteIcon toSpriteIcon(PointerIcon pointerIcon) { // to the underlying bitmap, so even if the java object is released, we will still have access // to it. return SpriteIcon(pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, - pointerIcon.hotSpotY); + pointerIcon.hotSpotY, pointerIcon.drawNativeDropShadow); } enum { @@ -1760,13 +1760,14 @@ void NativeInputManager::loadAdditionalMouseResources( animationData.durationPerFrame = milliseconds_to_nanoseconds(pointerIcon.durationPerFrame); animationData.animationFrames.reserve(numFrames); - animationData.animationFrames.push_back(SpriteIcon( - pointerIcon.bitmap, pointerIcon.style, - pointerIcon.hotSpotX, pointerIcon.hotSpotY)); + animationData.animationFrames.emplace_back(pointerIcon.bitmap, pointerIcon.style, + pointerIcon.hotSpotX, pointerIcon.hotSpotY, + pointerIcon.drawNativeDropShadow); for (size_t i = 0; i < numFrames - 1; ++i) { - animationData.animationFrames.push_back(SpriteIcon( - pointerIcon.bitmapFrames[i], pointerIcon.style, - pointerIcon.hotSpotX, pointerIcon.hotSpotY)); + animationData.animationFrames.emplace_back(pointerIcon.bitmapFrames[i], + pointerIcon.style, pointerIcon.hotSpotX, + pointerIcon.hotSpotY, + pointerIcon.drawNativeDropShadow); } } } @@ -2610,7 +2611,7 @@ static bool nativeSetPointerIcon(JNIEnv* env, jobject nativeImplObj, jobject ico icon = std::make_unique<SpriteIcon>(pointerIcon.bitmap.copy( ANDROID_BITMAP_FORMAT_RGBA_8888), pointerIcon.style, pointerIcon.hotSpotX, - pointerIcon.hotSpotY); + pointerIcon.hotSpotY, pointerIcon.drawNativeDropShadow); } else { icon = pointerIcon.style; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6f0985aecebd..f87fd8dd0da4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -6016,10 +6016,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Make sure the caller has any active admin with the right policy or // the required permission. if (isUnicornFlagEnabled()) { - admin = enforcePermissionAndGetEnforcingAdmin( + admin = enforcePermissionsAndGetEnforcingAdmin( /* admin= */ null, - /* permission= */ MANAGE_DEVICE_POLICY_LOCK, - USES_POLICY_FORCE_LOCK, + /* permissions= */ new String[]{MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE}, + /* deviceAdminPolicy= */ USES_POLICY_FORCE_LOCK, caller.getPackageName(), getAffectedUser(parent) ).getActiveAdmin(); 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 097d73a9a05b..1241ce60af12 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 @@ -423,6 +423,7 @@ class PermissionService(private val service: AccessCheckingService) : with(policy) { removePermission(permission) } } } + private fun GetStateScope.getAndEnforcePermissionTree(permissionName: String): Permission { val callingUid = Binder.getCallingUid() val permissionTree = with(policy) { findPermissionTree(permissionName) } @@ -486,9 +487,16 @@ 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, deviceId) + isPermissionGranted(packageState, userId, permissionName, persistentDeviceId) } return if (isPermissionGranted) { PackageManager.PERMISSION_GRANTED @@ -522,7 +530,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun checkPermission( packageName: String, permissionName: String, - deviceId: Int, + persistentDeviceId: String, userId: Int ): Int { if (!userManagerInternal.exists(userId)) { @@ -536,7 +544,9 @@ class PermissionService(private val service: AccessCheckingService) : ?: return PackageManager.PERMISSION_DENIED val isPermissionGranted = - service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) } + service.getState { + isPermissionGranted(packageState, userId, permissionName, persistentDeviceId) + } return if (isPermissionGranted) { PackageManager.PERMISSION_GRANTED } else { @@ -554,13 +564,21 @@ class PermissionService(private val service: AccessCheckingService) : packageState: PackageState, userId: Int, permissionName: String, - deviceId: Int + persistentDeviceId: 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, deviceId)) { + if ( + isSinglePermissionGranted( + appId, + userId, + isInstantApp, + permissionName, + persistentDeviceId + ) + ) { return true } @@ -572,7 +590,7 @@ class PermissionService(private val service: AccessCheckingService) : userId, isInstantApp, fullerPermissionName, - deviceId + persistentDeviceId ) ) { return true @@ -587,9 +605,9 @@ class PermissionService(private val service: AccessCheckingService) : userId: Int, isInstantApp: Boolean, permissionName: String, - deviceId: Int, + persistentDeviceId: String, ): Boolean { - val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) + val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) if (!PermissionFlags.isPermissionGranted(flags)) { return false } @@ -626,7 +644,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState, userId, permissionName, - Context.DEVICE_ID_DEFAULT + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT ) ) { permissionName @@ -670,16 +688,22 @@ class PermissionService(private val service: AccessCheckingService) : override fun grantRuntimePermission( packageName: String, permissionName: String, - deviceId: Int, + persistentDeviceId: String, userId: Int ) { - setRuntimePermissionGranted(packageName, userId, permissionName, deviceId, isGranted = true) + setRuntimePermissionGranted( + packageName, + userId, + permissionName, + persistentDeviceId, + isGranted = true + ) } override fun revokeRuntimePermission( packageName: String, permissionName: String, - deviceId: Int, + persistentDeviceId: String, userId: Int, reason: String? ) { @@ -687,7 +711,7 @@ class PermissionService(private val service: AccessCheckingService) : packageName, userId, permissionName, - deviceId, + persistentDeviceId, isGranted = false, revokeReason = reason ) @@ -701,7 +725,7 @@ class PermissionService(private val service: AccessCheckingService) : packageName, userId, Manifest.permission.POST_NOTIFICATIONS, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, isGranted = false, skipKillUid = true ) @@ -715,7 +739,7 @@ class PermissionService(private val service: AccessCheckingService) : packageName: String, userId: Int, permissionName: String, - deviceId: Int, + persistentDeviceId: String, isGranted: Boolean, skipKillUid: Boolean = false, revokeReason: String? = null @@ -739,7 +763,8 @@ class PermissionService(private val service: AccessCheckingService) : " permissionName = $permissionName" + (if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") + ", userId = $userId," + - " callingUid = $callingUidName ($callingUid))", + " callingUid = $callingUidName ($callingUid))," + + " persistentDeviceId = $persistentDeviceId", RuntimeException() ) } @@ -809,7 +834,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState, userId, permissionName, - deviceId, + persistentDeviceId, isGranted, canManageRolePermission, overridePolicyFixed, @@ -853,7 +878,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState, userId, permissionName, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, isGranted = true, canManageRolePermission = false, overridePolicyFixed = false, @@ -864,7 +889,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState.appId, userId, permissionName, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0, @@ -897,7 +922,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState: PackageState, userId: Int, permissionName: String, - deviceId: Int, + persistentDeviceId: String, isGranted: Boolean, canManageRolePermission: Boolean, overridePolicyFixed: Boolean, @@ -956,12 +981,14 @@ class PermissionService(private val service: AccessCheckingService) : } val appId = packageState.appId - val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) + val oldFlags = + getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) { if (reportError) { Slog.e( - LOG_TAG, "Permission $permissionName isn't requested by package $packageName" + LOG_TAG, + "Permission $permissionName isn't requested by package $packageName" ) } return @@ -1027,7 +1054,7 @@ class PermissionService(private val service: AccessCheckingService) : return } - setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags) + setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags) if (permission.isRuntime) { val action = @@ -1061,7 +1088,7 @@ class PermissionService(private val service: AccessCheckingService) : override fun getPermissionFlags( packageName: String, permissionName: String, - deviceId: Int, + persistentDeviceId: String, userId: Int, ): Int { if (!userManagerInternal.exists(userId)) { @@ -1097,7 +1124,12 @@ class PermissionService(private val service: AccessCheckingService) : } val flags = - getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId) + getPermissionFlagsWithPolicy( + packageState.appId, + userId, + permissionName, + persistentDeviceId + ) return PermissionFlags.toApiFlags(flags) } @@ -1127,13 +1159,24 @@ 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, deviceId)) { + if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) { return false } val flags = - getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId) + getPermissionFlagsWithPolicy( + packageState.appId, + userId, + permissionName, + persistentDeviceId + ) return flags.hasBits(PermissionFlags.POLICY_FIXED) } @@ -1183,13 +1226,19 @@ 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, deviceId)) { + if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) { return false } - flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) + flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) } if (flags.hasAnyBit(UNREQUESTABLE_MASK)) { return false @@ -1228,7 +1277,7 @@ class PermissionService(private val service: AccessCheckingService) : flagMask: Int, flagValues: Int, enforceAdjustPolicyPermission: Boolean, - deviceId: Int, + persistentDeviceId: String, userId: Int ) { val callingUid = Binder.getCallingUid() @@ -1254,6 +1303,7 @@ class PermissionService(private val service: AccessCheckingService) : "updatePermissionFlags(packageName = $packageName," + " permissionName = $permissionName, flagMask = $flagMaskString," + " flagValues = $flagValuesString, userId = $userId," + + " persistentDeviceId = $persistentDeviceId," + " callingUid = $callingUidName ($callingUid))", RuntimeException() ) @@ -1343,7 +1393,7 @@ class PermissionService(private val service: AccessCheckingService) : appId, userId, permissionName, - deviceId, + persistentDeviceId, flagMask, flagValues, canUpdateSystemFlags, @@ -1410,7 +1460,7 @@ class PermissionService(private val service: AccessCheckingService) : packageState.appId, userId, permissionName, - Context.DEVICE_ID_DEFAULT, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, flagMask, flagValues, canUpdateSystemFlags, @@ -1429,7 +1479,7 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - deviceId: Int, + persistentDeviceId: String, flagMask: Int, flagValues: Int, canUpdateSystemFlags: Boolean, @@ -1463,7 +1513,8 @@ class PermissionService(private val service: AccessCheckingService) : return } - val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId) + val oldFlags = + getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId) if (!isPermissionRequested && oldFlags == 0) { Slog.w( LOG_TAG, @@ -1474,7 +1525,7 @@ class PermissionService(private val service: AccessCheckingService) : } val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues) - setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags) + setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags) } override fun getAllowlistedRestrictedPermissions( @@ -1549,10 +1600,12 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - deviceId: Int, + persistentDeviceId: String, ): Int { - return if (!Flags.deviceAwarePermissionApisEnabled() || - deviceId == Context.DEVICE_ID_DEFAULT) { + return if ( + !Flags.deviceAwarePermissionApisEnabled() || + persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + ) { with(policy) { getPermissionFlags(appId, userId, permissionName) } } else { if (permissionName !in DEVICE_AWARE_PERMISSIONS) { @@ -1563,19 +1616,8 @@ class PermissionService(private val service: AccessCheckingService) : ) return with(policy) { getPermissionFlags(appId, userId, permissionName) } } - val virtualDeviceManagerInternal = virtualDeviceManagerInternal - if (virtualDeviceManagerInternal == null) { - Slog.e(LOG_TAG, "Virtual device manager service is not available.") - return 0 - } - val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId) - if (persistentDeviceId != null) { - with(devicePolicy) { - getPermissionFlags(appId, persistentDeviceId, userId, permissionName) - } - } else { - Slog.e(LOG_TAG, "Invalid device ID $deviceId.") - 0 + with(devicePolicy) { + getPermissionFlags(appId, persistentDeviceId, userId, permissionName) } } } @@ -1584,11 +1626,13 @@ class PermissionService(private val service: AccessCheckingService) : appId: Int, userId: Int, permissionName: String, - deviceId: Int, + persistentDeviceId: String, flags: Int ): Boolean { - return if (!Flags.deviceAwarePermissionApisEnabled() || - deviceId == Context.DEVICE_ID_DEFAULT) { + return if ( + !Flags.deviceAwarePermissionApisEnabled() || + persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT + ) { with(policy) { setPermissionFlags(appId, userId, permissionName, flags) } } else { if (permissionName !in DEVICE_AWARE_PERMISSIONS) { @@ -1600,23 +1644,24 @@ class PermissionService(private val service: AccessCheckingService) : return with(policy) { setPermissionFlags(appId, userId, permissionName, flags) } } - val virtualDeviceManagerInternal = virtualDeviceManagerInternal - if (virtualDeviceManagerInternal == null) { - Slog.e(LOG_TAG, "Virtual device manager service is not available.") - return false - } - val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId) - if (persistentDeviceId != null) { - with(devicePolicy) { - setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags) - } - } else { - Slog.e(LOG_TAG, "Invalid device ID $deviceId.") - false + with(devicePolicy) { + setPermissionFlags(appId, persistentDeviceId, 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. diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt index 2c8b1cd884f1..349b83167793 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt @@ -54,7 +54,8 @@ class ParsedActivityTest : ParsedMainComponentTest( ParsedActivity::getTheme, ParsedActivity::getUiOptions, ParsedActivity::isSupportsSizeChanges, - ParsedActivity::getRequiredDisplayCategory + ParsedActivity::getRequiredDisplayCategory, + ParsedActivity::getRequireContentUriPermissionFromCaller ) override fun mainComponentSubclassExtraParams() = listOf( diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 9f97551bf5e7..53c19607afec 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -54,6 +54,7 @@ android_test { "mockito-target-extended-minus-junit4", "platform-compat-test-rules", "platform-test-annotations", + "PlatformProperties", "service-blobstore", "service-jobscheduler", "service-permission.impl", diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 7b771aff0055..2d065e263a6f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -45,6 +45,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; +import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import com.android.dx.mockito.inline.extended.ExtendedMockito; @@ -95,7 +96,6 @@ public class RescuePartyTest { "persist.device_config.configuration.disable_rescue_party"; private static final String PROP_DISABLE_FACTORY_RESET_FLAG = "persist.device_config.configuration.disable_rescue_party_factory_reset"; - private static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset"; private static final int THROTTLING_DURATION_MIN = 10; @@ -211,8 +211,8 @@ public class RescuePartyTest { doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); - SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0)); - SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + CrashRecoveryProperties.rescueBootCount(0); + CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } @@ -255,7 +255,7 @@ public class RescuePartyTest { noteBoot(4); assertTrue(RescueParty.isRebootPropertySet()); - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); noteBoot(5); assertTrue(RescueParty.isFactoryResetPropertySet()); } @@ -280,7 +280,7 @@ public class RescuePartyTest { noteAppCrash(4, true); assertTrue(RescueParty.isRebootPropertySet()); - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); noteAppCrash(5, true); assertTrue(RescueParty.isFactoryResetPropertySet()); } @@ -438,7 +438,7 @@ public class RescuePartyTest { noteBoot(i + 1); } assertFalse(RescueParty.isFactoryResetPropertySet()); - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); noteBoot(LEVEL_FACTORY_RESET + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); @@ -456,7 +456,7 @@ public class RescuePartyTest { noteBoot(mitigationCount++); assertFalse(RescueParty.isFactoryResetPropertySet()); noteBoot(mitigationCount++); - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); noteBoot(mitigationCount + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); @@ -464,10 +464,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnBootFailures() { - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); - SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout)); + CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } @@ -476,10 +476,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnAppCrash() { - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); - SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout)); + CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } @@ -488,10 +488,10 @@ public class RescuePartyTest { @Test public void testNotThrottlingAfterTimeoutOnBootFailures() { - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); - SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout)); + CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } @@ -499,10 +499,10 @@ public class RescuePartyTest { } @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { - SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false)); + CrashRecoveryProperties.attemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); - SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout)); + CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } @@ -525,26 +525,26 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { - SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); + CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); - SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + CrashRecoveryProperties.enableRescueParty(true); assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1)); } @Test public void testDisablingRescueByDeviceConfigFlag() { - SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); + CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); // Restore the property value initialized in SetUp() - SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index bb91939c430e..067dd3bf1f7d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -113,6 +113,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.role.RoleManager; import android.app.usage.AppStandbyInfo; +import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -2439,7 +2440,8 @@ public final class BackgroundRestrictionTest { doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED) .when(mPermissionManagerServiceInternal) .checkPermission( - packageName, perm, Context.DEVICE_ID_DEFAULT, UserHandle.getUserId(uid)); + packageName, perm, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, + UserHandle.getUserId(uid)); try { doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED) .when(mIActivityManager) diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index ad6e2c657c96..3743483377b5 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -154,6 +154,7 @@ android_ravenwood_test { "androidx.annotation_annotation", "androidx.test.rules", "services.core", + "flag-junit", ], srcs: [ "src/com/android/server/uri/**/*.java", diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 99fa30c588db..1d3daccd0d90 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -169,14 +169,14 @@ public class HdmiControlServiceTest { .setEarcSupported(true) .build(); mHdmiPortInfo[3] = - new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000) + new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000) .setCecSupported(true) .setMhlSupported(false) .setArcSupported(false) .setEarcSupported(false) .build(); mHdmiPortInfo[4] = - new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000) + new HdmiPortInfo.Builder(5, HdmiPortInfo.PORT_OUTPUT, 0x3000) .setCecSupported(true) .setMhlSupported(false) .setArcSupported(false) @@ -841,6 +841,65 @@ public class HdmiControlServiceTest { } @Test + public void onHotPlugIn_CecDisabledOnTv_CecNotAvailable() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class); + mNativeWrapper.clearResultMessages(); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.onHotplug(4, true); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = + HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(), + Constants.ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + // Wait for DevicePowerStatusAction to finish. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse(); + } + + @Test + public void onHotPlugIn_CecEnabledOnTv_CecAvailable() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class); + mNativeWrapper.clearResultMessages(); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.onHotplug(4, true); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = + HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(), + Constants.ADDR_TV); + HdmiCecMessage reportPowerStatus = + HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, + mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(), + HdmiControlManager.POWER_STATUS_ON); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue(); + } + @Test public void handleCecCommand_errorParameter_returnsAbortInvalidOperand() { // Validity ERROR_PARAMETER. Taken from HdmiCecMessageValidatorTest#isValid_menuStatus HdmiCecMessage message = HdmiUtils.buildMessage("80:8D:03"); diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java index f537efd9736d..da8ec2e4ec15 100644 --- a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java @@ -17,12 +17,14 @@ package com.android.server.pdb; import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES; +import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_SIZE; import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE; import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE; import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; @@ -30,7 +32,8 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import static org.junit.Assert.assertThrows; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import android.Manifest; import android.content.Context; @@ -45,7 +48,6 @@ import androidx.test.core.app.ApplicationProvider; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -54,9 +56,13 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.File; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; @RunWith(JUnitParamsRunner.class) public class PersistentDataBlockServiceTest { @@ -64,20 +70,31 @@ public class PersistentDataBlockServiceTest { private static final byte[] SMALL_DATA = "data to write".getBytes(); private static final byte[] ANOTHER_SMALL_DATA = "something else".getBytes(); + public static final int DEFAULT_BLOCK_DEVICE_SIZE = -1; private Context mContext; private PersistentDataBlockService mPdbService; private IPersistentDataBlockService mInterface; private PersistentDataBlockManagerInternal mInternalInterface; private File mDataBlockFile; + private File mFrpSecretFile; + private File mFrpSecretTmpFile; private String mOemUnlockPropertyValue; + private boolean mIsUpgradingFromPreV = false; @Mock private UserManager mUserManager; private class FakePersistentDataBlockService extends PersistentDataBlockService { + FakePersistentDataBlockService(Context context, String dataBlockFile, - long blockDeviceSize) { - super(context, /* isFileBacked */ true, dataBlockFile, blockDeviceSize); + long blockDeviceSize, boolean frpEnabled, String frpSecretFile, + String frpSecretTmpFile) { + super(context, /* isFileBacked */ true, dataBlockFile, blockDeviceSize, frpEnabled, + frpSecretFile, frpSecretTmpFile); + // In the real service, this is done by onStart(), which we don't want to call because + // it registers the service, etc. But we need to signal init done to prevent + // `isFrpActive` from blocking. + signalInitDone(); } @Override @@ -86,18 +103,25 @@ public class PersistentDataBlockServiceTest { assertThat(key).isEqualTo("sys.oem_unlock_allowed"); mOemUnlockPropertyValue = value; } + + @Override + boolean isUpgradingFromPreVRelease() { + return mIsUpgradingFromPreV; + } } @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - @Before - public void setUp() throws Exception { + private void setUp(boolean frpEnabled) throws Exception { MockitoAnnotations.initMocks(this); mDataBlockFile = mTemporaryFolder.newFile(); + mFrpSecretFile = mTemporaryFolder.newFile(); + mFrpSecretTmpFile = mTemporaryFolder.newFile(); mContext = spy(ApplicationProvider.getApplicationContext()); mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(), - /* blockDeviceSize */ -1); + DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, mFrpSecretFile.getPath(), + mFrpSecretTmpFile.getPath()); mPdbService.setAllowedUid(Binder.getCallingUid()); mPdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false); mInterface = mPdbService.getInterfaceForTesting(); @@ -119,9 +143,7 @@ public class PersistentDataBlockServiceTest { * a block implementation for the read/write operations. */ public Object[][] getTestParametersForBlocks() { - return new Object[][] { - { - new Block() { + Block simpleReadWrite = new Block() { @Override public int write(byte[] data) throws RemoteException { return service.getInterfaceForTesting().write(data); } @@ -129,10 +151,8 @@ public class PersistentDataBlockServiceTest { @Override public byte[] read() throws RemoteException { return service.getInterfaceForTesting().read(); } - }, - }, - { - new Block() { + }; + Block credHandle = new Block() { @Override public int write(byte[] data) { service.getInternalInterfaceForTesting().setFrpCredentialHandle(data); // The written size isn't returned. Pretend it's fully written in the @@ -143,10 +163,8 @@ public class PersistentDataBlockServiceTest { @Override public byte[] read() { return service.getInternalInterfaceForTesting().getFrpCredentialHandle(); } - }, - }, - { - new Block() { + }; + Block testHarness = new Block() { @Override public int write(byte[] data) { service.getInternalInterfaceForTesting().setTestHarnessModeData(data); // The written size isn't returned. Pretend it's fully written in the @@ -157,14 +175,21 @@ public class PersistentDataBlockServiceTest { @Override public byte[] read() { return service.getInternalInterfaceForTesting().getTestHarnessModeData(); } - }, - }, + }; + return new Object[][] { + { simpleReadWrite, false }, + { simpleReadWrite, true }, + { credHandle, false }, + { credHandle, true }, + { testHarness, false }, + { testHarness, true }, }; } @Test @Parameters(method = "getTestParametersForBlocks") - public void writeThenRead(Block block) throws Exception { + public void writeThenRead(Block block, boolean frpEnabled) throws Exception { + setUp(frpEnabled); block.service = mPdbService; assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length); assertThat(block.read()).isEqualTo(SMALL_DATA); @@ -172,7 +197,8 @@ public class PersistentDataBlockServiceTest { @Test @Parameters(method = "getTestParametersForBlocks") - public void writeWhileAlreadyCorrupted(Block block) throws Exception { + public void writeWhileAlreadyCorrupted(Block block, boolean frpEnabled) throws Exception { + setUp(frpEnabled); block.service = mPdbService; assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length); assertThat(block.read()).isEqualTo(SMALL_DATA); @@ -184,7 +210,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void frpWriteOutOfBound() throws Exception { + @Parameters({"false", "true"}) + public void frpWriteOutOfBound(boolean frpEnabled) throws Exception { + setUp(frpEnabled); byte[] maxData = new byte[mPdbService.getMaximumFrpDataSize()]; assertThat(mInterface.write(maxData)).isEqualTo(maxData.length); @@ -193,7 +221,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void frpCredentialWriteOutOfBound() throws Exception { + @Parameters({"false", "true"}) + public void frpCredentialWriteOutOfBound(boolean frpEnabled) throws Exception { + setUp(frpEnabled); byte[] maxData = new byte[MAX_FRP_CREDENTIAL_HANDLE_SIZE]; mInternalInterface.setFrpCredentialHandle(maxData); @@ -203,7 +233,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void testHardnessWriteOutOfBound() throws Exception { + @Parameters({"false", "true"}) + public void testHardnessWriteOutOfBound(boolean frpEnabled) throws Exception { + setUp(frpEnabled); byte[] maxData = new byte[MAX_TEST_MODE_DATA_SIZE]; mInternalInterface.setTestHarnessModeData(maxData); @@ -213,7 +245,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void readCorruptedFrpData() throws Exception { + @Parameters({"false", "true"}) + public void readCorruptedFrpData(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length); assertThat(mInterface.read()).isEqualTo(SMALL_DATA); @@ -224,7 +258,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void readCorruptedFrpCredentialData() throws Exception { + @Parameters({"false", "true"}) + public void readCorruptedFrpCredentialData(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mInternalInterface.setFrpCredentialHandle(SMALL_DATA); assertThat(mInternalInterface.getFrpCredentialHandle()).isEqualTo(SMALL_DATA); @@ -235,7 +271,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void readCorruptedTestHarnessData() throws Exception { + @Parameters({"false", "true"}) + public void readCorruptedTestHarnessData(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mInternalInterface.setTestHarnessModeData(SMALL_DATA); assertThat(mInternalInterface.getTestHarnessModeData()).isEqualTo(SMALL_DATA); @@ -246,14 +284,18 @@ public class PersistentDataBlockServiceTest { } @Test - public void nullWrite() throws Exception { + @Parameters({"false", "true"}) + public void nullWrite(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThrows(NullPointerException.class, () -> mInterface.write(null)); mInternalInterface.setFrpCredentialHandle(null); // no exception mInternalInterface.setTestHarnessModeData(null); // no exception } @Test - public void emptyDataWrite() throws Exception { + @Parameters({"false", "true"}) + public void emptyDataWrite(boolean frpEnabled) throws Exception { + setUp(frpEnabled); var empty = new byte[0]; assertThat(mInterface.write(empty)).isEqualTo(0); @@ -264,10 +306,13 @@ public class PersistentDataBlockServiceTest { } @Test - public void frpWriteMoreThan100K() throws Exception { + @Parameters({"false", "true"}) + public void frpWriteMoreThan100K(boolean frpEnabled) throws Exception { + setUp(frpEnabled); File dataBlockFile = mTemporaryFolder.newFile(); PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext, - dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000); + dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000, frpEnabled, + /* frpSecretFile */ null, /* frpSecretTmpFile */ null); pdbService.setAllowedUid(Binder.getCallingUid()); pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false); @@ -278,30 +323,39 @@ public class PersistentDataBlockServiceTest { } @Test - public void frpBlockReadWriteWithoutPermission() throws Exception { + @Parameters({"false", "true"}) + public void frpBlockReadWriteWithoutPermission(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid assertThrows(SecurityException.class, () -> mInterface.write(SMALL_DATA)); assertThrows(SecurityException.class, () -> mInterface.read()); } @Test - public void getMaximumDataBlockSizeDenied() throws Exception { + @Parameters({"false", "true"}) + public void getMaximumDataBlockSizeDenied(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid assertThrows(SecurityException.class, () -> mInterface.getMaximumDataBlockSize()); } @Test - public void getMaximumDataBlockSize() throws Exception { + @Parameters({"false", "true"}) + public void getMaximumDataBlockSize(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mPdbService.setAllowedUid(Binder.getCallingUid()); assertThat(mInterface.getMaximumDataBlockSize()) .isEqualTo(mPdbService.getMaximumFrpDataSize()); } @Test - public void getMaximumDataBlockSizeOfLargerPartition() throws Exception { + @Parameters({"false", "true"}) + public void getMaximumDataBlockSizeOfLargerPartition(boolean frpEnabled) throws Exception { + setUp(frpEnabled); File dataBlockFile = mTemporaryFolder.newFile(); PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext, - dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000); + dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000, frpEnabled, + /* frpSecretFile */null, /* mFrpSecretTmpFile */ null); pdbService.setAllowedUid(Binder.getCallingUid()); pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false); @@ -310,7 +364,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void getFrpDataBlockSizeGrantedByUid() throws Exception { + @Parameters({"false", "true"}) + public void getFrpDataBlockSizeGrantedByUid(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length); mPdbService.setAllowedUid(Binder.getCallingUid()); @@ -323,7 +379,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void getFrpDataBlockSizeGrantedByPermission() throws Exception { + @Parameters({"false", "true"}) + public void getFrpDataBlockSizeGrantedByPermission(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length); mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid @@ -338,13 +396,17 @@ public class PersistentDataBlockServiceTest { } @Test - public void wipePermissionCheck() throws Exception { + @Parameters({"false", "true"}) + public void wipePermissionCheck(boolean frpEnabled) throws Exception { + setUp(frpEnabled); denyOemUnlockPermission(); assertThrows(SecurityException.class, () -> mInterface.wipe()); } @Test - public void wipeMakesItNotWritable() throws Exception { + @Parameters({"false", "true"}) + public void wipeMakesItNotWritable(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); mInterface.wipe(); @@ -368,7 +430,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void hasFrpCredentialHandleGrantedByUid() throws Exception { + @Parameters({"false", "true"}) + public void hasFrpCredentialHandle_GrantedByUid(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mPdbService.setAllowedUid(Binder.getCallingUid()); assertThat(mInterface.hasFrpCredentialHandle()).isFalse(); @@ -377,17 +441,51 @@ public class PersistentDataBlockServiceTest { } @Test - public void hasFrpCredentialHandleGrantedByPermission() throws Exception { + @Parameters({"false", "true"}) + public void hasFrpCredentialHandle_GrantedByConfigureFrpPermission(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); + grantConfigureFrpPermission(); + mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid + + if (frpEnabled) { + assertThat(mInterface.hasFrpCredentialHandle()).isFalse(); + mInternalInterface.setFrpCredentialHandle(SMALL_DATA); + assertThat(mInterface.hasFrpCredentialHandle()).isTrue(); + } else { + assertThrows(SecurityException.class, () -> mInterface.hasFrpCredentialHandle()); + } + } + + @Test + @Parameters({"false", "true"}) + public void hasFrpCredentialHandle_GrantedByAccessPdbStatePermission(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); grantAccessPdbStatePermission(); + mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid + assertThat(mInterface.hasFrpCredentialHandle()).isFalse(); mInternalInterface.setFrpCredentialHandle(SMALL_DATA); assertThat(mInterface.hasFrpCredentialHandle()).isTrue(); } @Test - public void clearTestHarnessModeData() throws Exception { + @Parameters({"false", "true"}) + public void hasFrpCredentialHandle_Unauthorized(boolean frpEnabled) throws Exception { + setUp(frpEnabled); + + mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid + + assertThrows(SecurityException.class, () -> mInterface.hasFrpCredentialHandle()); + } + + @Test + @Parameters({"false", "true"}) + public void clearTestHarnessModeData(boolean frpEnabled) throws Exception { + setUp(frpEnabled); mInternalInterface.setTestHarnessModeData(SMALL_DATA); mInternalInterface.clearTestHarnessModeData(); @@ -397,19 +495,25 @@ public class PersistentDataBlockServiceTest { } @Test - public void getAllowedUid() throws Exception { + @Parameters({"false", "true"}) + public void getAllowedUid(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThat(mInternalInterface.getAllowedUid()).isEqualTo(Binder.getCallingUid()); } @Test - public void oemUnlockWithoutPermission() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlockWithoutPermission(boolean frpEnabled) throws Exception { + setUp(frpEnabled); denyOemUnlockPermission(); assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true)); } @Test - public void oemUnlockNotAdmin() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlockNotAdmin(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); makeUserAdmin(false); @@ -417,7 +521,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void oemUnlock() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlock(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); makeUserAdmin(true); @@ -427,7 +533,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void oemUnlockUserRestriction_OemUnlock() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlockUserRestriction_OemUnlock(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); makeUserAdmin(true); when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_OEM_UNLOCK))) @@ -437,7 +545,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void oemUnlockUserRestriction_FactoryReset() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlockUserRestriction_FactoryReset(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); makeUserAdmin(true); when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_FACTORY_RESET))) @@ -447,7 +557,9 @@ public class PersistentDataBlockServiceTest { } @Test - public void oemUnlockIgnoreTampering() throws Exception { + @Parameters({"false", "true"}) + public void oemUnlockIgnoreTampering(boolean frpEnabled) throws Exception { + setUp(frpEnabled); grantOemUnlockPermission(); makeUserAdmin(true); @@ -460,26 +572,37 @@ public class PersistentDataBlockServiceTest { } @Test - public void getOemUnlockEnabledPermissionCheck_NoPermission() throws Exception { + @Parameters({"false", "true"}) + public void getOemUnlockEnabledPermissionCheck_NoPermission(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); assertThrows(SecurityException.class, () -> mInterface.getOemUnlockEnabled()); } @Test - public void getOemUnlockEnabledPermissionCheck_OemUnlcokState() throws Exception { + @Parameters({"false", "true"}) + public void getOemUnlockEnabledPermissionCheck_OemUnlockState(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext) .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE)); assertThat(mInterface.getOemUnlockEnabled()).isFalse(); } @Test - public void getOemUnlockEnabledPermissionCheck_ReadOemUnlcokState() throws Exception { + @Parameters({"false", "true"}) + public void getOemUnlockEnabledPermissionCheck_ReadOemUnlockState(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext) .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE)); assertThat(mInterface.getOemUnlockEnabled()).isFalse(); } @Test - public void forceOemUnlock_RequiresNoPermission() throws Exception { + @Parameters({"false", "true"}) + public void forceOemUnlock_RequiresNoPermission(boolean frpEnabled) throws Exception { + setUp(frpEnabled); denyOemUnlockPermission(); mInternalInterface.forceOemUnlockEnabled(true); @@ -490,24 +613,331 @@ public class PersistentDataBlockServiceTest { } @Test - public void getFlashLockStatePermissionCheck_NoPermission() throws Exception { + @Parameters({"false", "true"}) + public void getFlashLockStatePermissionCheck_NoPermission(boolean frpEnabled) throws Exception { + setUp(frpEnabled); assertThrows(SecurityException.class, () -> mInterface.getFlashLockState()); } @Test - public void getFlashLockStatePermissionCheck_OemUnlcokState() throws Exception { + @Parameters({"false", "true"}) + public void getFlashLockStatePermissionCheck_OemUnlockState(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext) .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE)); mInterface.getFlashLockState(); // Do not throw } @Test - public void getFlashLockStatePermissionCheck_ReadOemUnlcokState() throws Exception { + @Parameters({"false", "true"}) + public void getFlashLockStatePermissionCheck_ReadOemUnlockState(boolean frpEnabled) + throws Exception { + setUp(frpEnabled); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext) .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE)); mInterface.getFlashLockState(); // Do not throw } + @Test + @Parameters({"false", "true"}) + public void frpMagicTest(boolean frpEnabled) throws Exception { + setUp(frpEnabled); + byte[] magicField = mPdbService.readDataBlock(mPdbService.getFrpSecretMagicOffset(), + PersistentDataBlockService.FRP_SECRET_MAGIC.length); + if (frpEnabled) { + assertThat(magicField).isEqualTo(PersistentDataBlockService.FRP_SECRET_MAGIC); + } else { + assertThat(magicField).isNotEqualTo(PersistentDataBlockService.FRP_SECRET_MAGIC); + } + } + + @Test + public void frpSecret_StartsAsDefault() throws Exception { + setUp(/* frpEnabled */ true); + + byte[] secretField = mPdbService.readDataBlock( + mPdbService.getFrpSecretDataOffset(), PersistentDataBlockService.FRP_SECRET_SIZE); + assertThat(secretField).isEqualTo(new byte[PersistentDataBlockService.FRP_SECRET_SIZE]); + } + + @Test + public void frpSecret_SetSecret() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + byte[] hashedSecret = hashStringto32Bytes("secret"); + assertThat(mInterface.setFactoryResetProtectionSecret(hashedSecret)).isTrue(); + + byte[] secretField = mPdbService.readDataBlock( + mPdbService.getFrpSecretDataOffset(), PersistentDataBlockService.FRP_SECRET_SIZE); + assertThat(secretField).isEqualTo(hashedSecret); + + assertThat(mFrpSecretFile.exists()).isTrue(); + byte[] secretFileData = Files.readAllBytes(mFrpSecretFile.toPath()); + assertThat(secretFileData).isEqualTo(hashedSecret); + + assertThat(mFrpSecretTmpFile.exists()).isFalse(); + } + + @Test + public void frpSecret_SetSecretByUnauthorizedCaller() throws Exception { + setUp(/* frpEnforcement */ true); + + mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid + assertThrows(SecurityException.class, + () -> mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret"))); + } + + /** + * Verify that FRP always starts in active state (if flag-enabled), until something is done to + * deactivate it. + */ + @Test + @Parameters({"false", "true"}) + public void frpState_StartsActive(boolean frpEnabled) throws Exception { + setUp(frpEnabled); + // Create a service without calling formatPartition, which deactivates FRP. + PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext, + mDataBlockFile.getPath(), DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, + mFrpSecretFile.getPath(), mFrpSecretTmpFile.getPath()); + assertThat(pdbService.isFrpActive()).isEqualTo(frpEnabled); + } + + @Test + public void frpState_AutomaticallyDeactivateWithDefault() throws Exception { + setUp(/* frpEnforcement */ true); + + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpState_AutomaticallyDeactivateWithPrimaryDataFile() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret")); + + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpState_AutomaticallyDeactivateWithBackupDataFile() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret")); + Files.move(mFrpSecretFile.toPath(), mFrpSecretTmpFile.toPath(), REPLACE_EXISTING); + + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpState_DeactivateWithSecret() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret")); + simulateDataWipe(); + + assertThat(mPdbService.isFrpActive()).isFalse(); + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isFalse(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mInterface.deactivateFactoryResetProtection(hashStringto32Bytes("wrongSecret"))) + .isFalse(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mInterface.deactivateFactoryResetProtection(hashStringto32Bytes("secret"))) + .isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + + assertThat(mInterface.setFactoryResetProtectionSecret(new byte[FRP_SECRET_SIZE])).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpState_DeactivateOnUpgradeFromPreV() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret")); + // If the /data files are still present, deactivation will use them. We want to verify + // that deactivation will succeed even if they are not present, so remove them. + simulateDataWipe(); + + // Verify that automatic deactivation fails without the /data files when we're not + // upgrading from pre-V. + mPdbService.activateFrp(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isFalse(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + // Verify that automatic deactivation succeeds when upgrading from pre-V. + mIsUpgradingFromPreV = true; + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + /** + * There is code in PersistentDataBlockService to handle a specific corner case, that of a + * device that is upgraded from pre-V to V+, downgraded to pre-V and then upgraded to V+. In + * this scenario, the following happens: + * + * 1. When the device is upgraded to V+ and the user sets an LSKF and GAIA creds, FRP + * enforcement is activated and three copies of the FRP secret are written to: + * a. The FRP secret field in PDB (plaintext). + * b. The GAIA challenge in PDB (encrypted). + * c. The FRP secret file in /data (plaintext). + * 2. When the device is downgraded to pre-V, /data is wiped, so copy (c) is destroyed. When the + * user sets LSKF and GAIA creds, copy (b) is overwritten. Copy (a) survives. + * 3. When the device is upgraded to V and boots the first time, FRP cannot be automatically + * deactivated using copy (c), nor can the user deactivate FRP using copy (b), because both + * are gone. Absent some special handling of this case, the device would be unusable. + * + * To address this problem, if PersistentDataBlockService finds an FRP secret in (a) but none + * in (b) or (c), and PackageManager reports that the device has just upgraded from pre-V to + * V+, it zeros the FRP secret in (a). + * + * This test checks that the service handles this sequence of events correctly. + */ + @Test + public void frpState_TestDowngradeUpgradeSequence() throws Exception { + // Simulate device in V+, with FRP configured. + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + assertThat(mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret"))) + .isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + + // Simulate reboot, still in V+. + boolean frpEnabled = true; + mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(), + DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, mFrpSecretFile.getPath(), + mFrpSecretTmpFile.getPath()); + assertThat(mPdbService.isFrpActive()).isTrue(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + + // Simulate reboot after data wipe and downgrade to pre-V. + simulateDataWipe(); + frpEnabled = false; + mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(), + DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, mFrpSecretFile.getPath(), + mFrpSecretTmpFile.getPath()); + assertThat(mPdbService.isFrpActive()).isFalse(); + + // Simulate reboot after upgrade to V+, no data wipe. + frpEnabled = true; + mIsUpgradingFromPreV = true; + mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(), + DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, mFrpSecretTmpFile.getPath(), + mFrpSecretTmpFile.getPath()); + mPdbService.setAllowedUid(Binder.getCallingUid()); // Needed for setFrpSecret(). + assertThat(mPdbService.isFrpActive()).isTrue(); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + assertThat(mPdbService.getInterfaceForTesting() + .setFactoryResetProtectionSecret(new byte[FRP_SECRET_SIZE])).isTrue(); + + // Simulate one more reboot. + mIsUpgradingFromPreV = false; + mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(), + DEFAULT_BLOCK_DEVICE_SIZE, frpEnabled, mFrpSecretTmpFile.getPath(), + mFrpSecretTmpFile.getPath()); + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpState_PrivilegedDeactivationByAuthorizedCaller() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + assertThat(mPdbService.isFrpActive()).isFalse(); + assertThat(mInterface.setFactoryResetProtectionSecret(hashStringto32Bytes("secret"))) + .isTrue(); + + simulateDataWipe(); + mPdbService.activateFrp(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isFalse(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + assertThat(mInternalInterface.deactivateFactoryResetProtectionWithoutSecret()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + @Test + public void frpActive_WipeFails() throws Exception { + setUp(/* frpEnforcement */ true); + + grantOemUnlockPermission(); + mPdbService.activateFrp(); + SecurityException e = assertThrows(SecurityException.class, () -> mInterface.wipe()); + assertThat(e).hasMessageThat().contains("FRP is active"); + } + + @Test + public void frpActive_WriteFails() throws Exception { + setUp(/* frpEnforcement */ true); + + mPdbService.activateFrp(); + SecurityException e = + assertThrows(SecurityException.class, () -> mInterface.write("data".getBytes())); + assertThat(e).hasMessageThat().contains("FRP is active"); + } + + @Test + public void frpActive_SetSecretFails() throws Exception { + setUp(/* frpEnforcement */ true); + grantConfigureFrpPermission(); + + mPdbService.activateFrp(); + + byte[] hashedSecret = hashStringto32Bytes("secret"); + SecurityException e = assertThrows(SecurityException.class, () + -> mInterface.setFactoryResetProtectionSecret(hashedSecret)); + assertThat(e).hasMessageThat().contains("FRP is active"); + assertThat(mPdbService.isFrpActive()).isTrue(); + + // Verify that secret we failed to set isn't accepted. + assertThat(mInterface.deactivateFactoryResetProtection(hashedSecret)).isFalse(); + assertThat(mPdbService.isFrpActive()).isTrue(); + + // Default should work, since it should never have been changed. + assertThat(mPdbService.automaticallyDeactivateFrpIfPossible()).isTrue(); + assertThat(mPdbService.isFrpActive()).isFalse(); + } + + private void simulateDataWipe() throws IOException { + Files.deleteIfExists(mFrpSecretFile.toPath()); + Files.deleteIfExists(mFrpSecretTmpFile.toPath()); + } + + private static byte[] hashStringto32Bytes(String secret) throws NoSuchAlgorithmException { + return MessageDigest.getInstance("SHA-256").digest(secret.getBytes()); + } + private void tamperWithDigest() throws Exception { try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE)) { ch.write(ByteBuffer.wrap("tampered-digest".getBytes())); @@ -542,6 +972,14 @@ public class PersistentDataBlockServiceTest { .checkCallingPermission(eq(Manifest.permission.ACCESS_PDB_STATE)); } + private void grantConfigureFrpPermission() { + doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( + eq(Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION)); + doNothing().when(mContext).enforceCallingOrSelfPermission( + eq(Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION), + anyString()); + } + private ByteBuffer readBackingFile(long position, int size) throws Exception { try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ)) { var buffer = ByteBuffer.allocate(size); diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index bf87e3ac1f7e..1ae6e63c3ff1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -407,7 +407,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_DENIED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); assertEquals( @@ -420,7 +420,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); assertEquals( @@ -433,7 +433,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); assertEquals( @@ -446,7 +446,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -473,7 +473,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -502,7 +502,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -540,7 +540,7 @@ public final class BackgroundInstallControlServiceTest { 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); @@ -624,7 +624,7 @@ public final class BackgroundInstallControlServiceTest { // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -673,7 +673,7 @@ public final class BackgroundInstallControlServiceTest { // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -727,7 +727,7 @@ public final class BackgroundInstallControlServiceTest { // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_2, @@ -782,7 +782,7 @@ public final class BackgroundInstallControlServiceTest { // install getBackgroundInstalledPackages() is expected to return null doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, @@ -835,7 +835,7 @@ public final class BackgroundInstallControlServiceTest { // install getBackgroundInstalledPackages() is expected to return null doReturn(PERMISSION_GRANTED) .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); + .checkPermission(anyString(), anyString(), anyString(), anyInt()); generateUsageEvent( UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java index 321858685e38..24abc183cad1 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.uri; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT; + import static com.android.server.uri.UriGrantsMockContext.FLAG_PERSISTABLE; import static com.android.server.uri.UriGrantsMockContext.FLAG_PREFIX; import static com.android.server.uri.UriGrantsMockContext.FLAG_READ; @@ -57,22 +59,49 @@ import android.content.pm.ProviderInfo; import android.net.Uri; import android.os.Process; import android.os.UserHandle; +import android.platform.test.flag.junit.FlagsParameterization; +import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodRule; import android.util.ArraySet; -import androidx.test.InstrumentationRegistry; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; +import java.util.List; import java.util.Set; +@RunWith(Parameterized.class) public class UriGrantsManagerServiceTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); + /** + * Why this class needs to test all combinations of + * {@link android.security.Flags#FLAG_CONTENT_URI_PERMISSION_APIS}: + * + * <p>Although tests in this class don't directly query the flag, its value + * is needed for {@link UriGrantsManagerInternal#checkGrantUriPermissionFromIntent}. This is + * particularly important for host side tests (Ravenwood), which cannot read flag values from + * the device and must have them set explicitly. + */ + @Parameters(name = "{0}") + public static List<FlagsParameterization> getFlags() { + return FlagsParameterization.allCombinationsOf( + android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS); + } + + public UriGrantsManagerServiceTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT, flags); + } + + @Rule + public final SetFlagsRule mSetFlagsRule; + private UriGrantsMockContext mContext; private UriGrantsManagerInternal mService; diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java index 4d4f5ed15ad6..611c51463246 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java @@ -33,8 +33,10 @@ import static com.android.server.uri.UriPermission.STRENGTH_PERSISTABLE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import android.os.SystemClock; import android.platform.test.ravenwood.RavenwoodRule; import org.junit.Before; @@ -154,10 +156,12 @@ public class UriPermissionTest { assertEquals(FLAG_WRITE, perm.persistableModeFlags); assertEquals(FLAG_WRITE, perm.persistedModeFlags); - // Attempting to take a second time should be a no-op + // Attempting to take a second time should "touch" timestamp, per public API + // docs on ContentResolver.takePersistableUriPermission() final long createTime = perm.persistedCreateTime; + SystemClock.sleep(10); assertFalse(perm.takePersistableModes(FLAG_WRITE)); - assertEquals(createTime, perm.persistedCreateTime); + assertNotEquals(createTime, perm.persistedCreateTime); assertTrue(perm.releasePersistableModes(FLAG_WRITE)); assertEquals(FLAG_WRITE, perm.persistableModeFlags); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 3034942953a1..2f52d5c48b6b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; +import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -246,9 +247,11 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, true); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); + USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test @@ -258,15 +261,17 @@ public class PermissionHelperTest extends UiServiceTestCase { .thenReturn(PERMISSION_DENIED); when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); + anyString(), anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( "pkg", 10, true, false); mPermissionHelper.setNotificationPermission(pkgPerm); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); + USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test @@ -278,9 +283,10 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), - eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); + eq(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); + USER_FLAG_MASK, FLAG_PERMISSION_USER_SET, true, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test @@ -291,9 +297,10 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, false); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10); + USER_FLAG_MASK, 0, true, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test @@ -305,35 +312,35 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), - eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); + eq(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10); + USER_FLAG_MASK, 0, true, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test public void testSetNotificationPermission_SystemFixedPermNotSet() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); + anyString(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); mPermissionHelper.setNotificationPermission("pkg", 10, false, true); verify(mPermManager, never()).revokeRuntimePermission( - anyString(), anyString(), anyInt(), anyInt(), anyString()); + anyString(), anyString(), anyString(), anyInt(), anyString()); verify(mPermManager, never()).updatePermissionFlags( - anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt()); + anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt()); } @Test public void testSetNotificationPermission_PolicyFixedPermNotSet() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED); + anyString(), anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED); mPermissionHelper.setNotificationPermission("pkg", 10, false, true); verify(mPermManager, never()).revokeRuntimePermission( - anyString(), anyString(), anyInt(), anyInt(), anyString()); + anyString(), anyString(), anyString(), anyInt(), anyString()); verify(mPermManager, never()).updatePermissionFlags( - anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt()); + anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt()); } @Test @@ -343,7 +350,8 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, false); verify(mPermManager, never()).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, 10); } @Test @@ -354,7 +362,7 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager, never()).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), - eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); + eq(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT), eq(10), anyString()); } @Test @@ -365,7 +373,7 @@ public class PermissionHelperTest extends UiServiceTestCase { when(mPackageManager.getPackageUid(anyString(), anyInt(), anyInt())) .thenReturn(testUid); PackageInfo testPkgInfo = new PackageInfo(); - testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.RECORD_AUDIO }; + testPkgInfo.requestedPermissions = new String[]{Manifest.permission.RECORD_AUDIO}; when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt())) .thenReturn(testPkgInfo); mPermissionHelper.setNotificationPermission("pkg", 10, false, false); @@ -374,26 +382,26 @@ public class PermissionHelperTest extends UiServiceTestCase { eq(Manifest.permission.POST_NOTIFICATIONS), eq(-1), eq(testUid)); verify(mPermManager, never()).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), - eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); + eq(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT), eq(10), anyString()); } @Test public void testIsPermissionFixed() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_USER_SET); + anyString(), anyInt())).thenReturn(FLAG_PERMISSION_USER_SET); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isFalse(); when(mPermManager.getPermissionFlags(anyString(), - eq(Manifest.permission.POST_NOTIFICATIONS), anyInt(), - anyInt())).thenReturn(FLAG_PERMISSION_USER_SET|FLAG_PERMISSION_POLICY_FIXED); + eq(Manifest.permission.POST_NOTIFICATIONS), anyString(), + anyInt())).thenReturn(FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_POLICY_FIXED); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue(); when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); + anyString(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue(); } @@ -435,19 +443,19 @@ public class PermissionHelperTest extends UiServiceTestCase { // 2 and 3 are user-set permissions when(mPermManager.getPermissionFlags("first", Manifest.permission.POST_NOTIFICATIONS, - Context.DEVICE_ID_DEFAULT, userId)) + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId)) .thenReturn(0); when(mPermManager.getPermissionFlags("second", Manifest.permission.POST_NOTIFICATIONS, - Context.DEVICE_ID_DEFAULT, userId)) + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId)) .thenReturn(FLAG_PERMISSION_USER_SET); when(mPermManager.getPermissionFlags("third", Manifest.permission.POST_NOTIFICATIONS, - Context.DEVICE_ID_DEFAULT, userId)) + VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId)) .thenReturn(FLAG_PERMISSION_USER_SET); Map<Pair<Integer, String>, Pair<Boolean, Boolean>> expected = ImmutableMap.of(new Pair(1, "first"), new Pair(true, false), - new Pair(2, "second"), new Pair(true, true), - new Pair(3, "third"), new Pair(false, true)); + new Pair(2, "second"), new Pair(true, true), + new Pair(3, "third"), new Pair(false, true)); Map<Pair<Integer, String>, Pair<Boolean, Boolean>> actual = mPermissionHelper.getNotificationPermissionValues(userId); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 90493d4dc95f..295b124c06e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -346,6 +346,7 @@ public class SystemServicesTestRule implements TestRule { doReturn(true).when(amInternal).hasStartedUserState(anyInt()); doReturn(false).when(amInternal).shouldConfirmCredentials(anyInt()); doReturn(false).when(amInternal).isActivityStartsLoggingEnabled(); + doReturn(true).when(amInternal).getThemeOverlayReadiness(); LocalServices.addService(ActivityManagerInternal.class, amInternal); final ActivityManagerService amService = diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e62bd90807d1..3daa0143f244 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2753,23 +2753,25 @@ public class TelecomManager { * * @param packageName the package name of the app to check calls for. * @param userHandle the user handle on which to check for calls. - * @param hasCrossUserAccess indicates if calls should be detected across all users. + * @param detectForAllUsers indicates if calls should be detected across all users. If it is + * set to {@code true}, and the caller has the ability to interact + * across users, the userHandle parameter is disregarded. * @return {@code true} if there are ongoing calls, {@code false} otherwise. + * @throws SecurityException if detectForAllUsers is true or userHandle is not the calling user + * and the caller does not grant the ability to interact across users. * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) - @RequiresPermission(allOf = { - android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, - Manifest.permission.INTERACT_ACROSS_USERS - }) + @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean isInSelfManagedCall(@NonNull String packageName, - @NonNull UserHandle userHandle, boolean hasCrossUserAccess) { + @NonNull UserHandle userHandle, boolean detectForAllUsers) { ITelecomService service = getTelecomService(); if (service != null) { try { return service.isInSelfManagedCall(packageName, userHandle, - mContext.getOpPackageName(), hasCrossUserAccess); + mContext.getOpPackageName(), detectForAllUsers); } catch (RemoteException e) { Log.e(TAG, "RemoteException isInSelfManagedCall: " + e); e.rethrowFromSystemServer(); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 412e8273b5e6..7dba799e1057 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -395,7 +395,7 @@ interface ITelecomService { * @see TelecomServiceImpl#isInSelfManagedCall */ boolean isInSelfManagedCall(String packageName, in UserHandle userHandle, - String callingPackage, boolean hasCrossUserAccess); + String callingPackage, boolean detectForAllUsers); /** * @see TelecomServiceImpl#addCall diff --git a/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java index e5a20ea0c358..bed9c45bbd5a 100644 --- a/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java +++ b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java @@ -32,6 +32,7 @@ import java.util.Objects; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class ActivityStatsTechSpecificInfo implements Parcelable { private static final int TX_POWER_LEVELS = 5; diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java index 9727ab7d23e2..b9b9680904bb 100644 --- a/telephony/java/android/telephony/CellSignalStrength.java +++ b/telephony/java/android/telephony/CellSignalStrength.java @@ -23,6 +23,7 @@ import android.os.PersistableBundle; /** * Abstract base class for cell phone signal strength related information. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public abstract class CellSignalStrength { public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java index 0f54e8d9457e..3c11da5f2daa 100644 --- a/telephony/java/android/telephony/DomainSelectionService.java +++ b/telephony/java/android/telephony/DomainSelectionService.java @@ -90,7 +90,7 @@ import java.util.function.Consumer; */ @SystemApi @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) -public class DomainSelectionService extends Service { +public abstract class DomainSelectionService extends Service { private static final String LOG_TAG = "DomainSelectionService"; @@ -152,7 +152,7 @@ public class DomainSelectionService extends Service { private boolean mIsExitedFromAirplaneMode; private @Nullable ImsReasonInfo mImsReasonInfo; private @PreciseDisconnectCauses int mCause; - private @Nullable EmergencyRegResult mEmergencyRegResult; + private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult; /** * @param slotIndex The logical slot index. @@ -172,7 +172,7 @@ public class DomainSelectionService extends Service { @Nullable Uri address, @SelectorType int selectorType, boolean video, boolean emergency, boolean isTest, boolean exited, @Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause, - @Nullable EmergencyRegResult regResult) { + @Nullable EmergencyRegistrationResult regResult) { mSlotIndex = slotIndex; mSubId = subscriptionId; mCallId = callId; @@ -184,7 +184,7 @@ public class DomainSelectionService extends Service { mIsExitedFromAirplaneMode = exited; mImsReasonInfo = imsReasonInfo; mCause = cause; - mEmergencyRegResult = regResult; + mEmergencyRegistrationResult = regResult; } /** @@ -204,7 +204,7 @@ public class DomainSelectionService extends Service { mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode; mImsReasonInfo = s.mImsReasonInfo; mCause = s.mCause; - mEmergencyRegResult = s.mEmergencyRegResult; + mEmergencyRegistrationResult = s.mEmergencyRegistrationResult; } /** @@ -296,8 +296,8 @@ public class DomainSelectionService extends Service { /** * @return The current registration state of cellular network. */ - public @Nullable EmergencyRegResult getEmergencyRegResult() { - return mEmergencyRegResult; + public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() { + return mEmergencyRegistrationResult; } @Override @@ -313,7 +313,7 @@ public class DomainSelectionService extends Service { + ", airplaneMode=" + mIsExitedFromAirplaneMode + ", reasonInfo=" + mImsReasonInfo + ", cause=" + mCause - + ", regResult=" + mEmergencyRegResult + + ", regResult=" + mEmergencyRegistrationResult + " }"; } @@ -331,14 +331,15 @@ public class DomainSelectionService extends Service { && mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode && equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo) && mCause == that.mCause - && equalsHandlesNulls(mEmergencyRegResult, that.mEmergencyRegResult); + && equalsHandlesNulls(mEmergencyRegistrationResult, + that.mEmergencyRegistrationResult); } @Override public int hashCode() { return Objects.hash(mCallId, mAddress, mImsReasonInfo, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode, - mEmergencyRegResult, mSlotIndex, mSubId, mSelectorType, mCause); + mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause); } @Override @@ -359,7 +360,7 @@ public class DomainSelectionService extends Service { out.writeBoolean(mIsExitedFromAirplaneMode); out.writeParcelable(mImsReasonInfo, 0); out.writeInt(mCause); - out.writeParcelable(mEmergencyRegResult, 0); + out.writeParcelable(mEmergencyRegistrationResult, 0); } private void readFromParcel(@NonNull Parcel in) { @@ -376,8 +377,9 @@ public class DomainSelectionService extends Service { mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(), android.telephony.ims.ImsReasonInfo.class); mCause = in.readInt(); - mEmergencyRegResult = in.readParcelable(EmergencyRegResult.class.getClassLoader(), - EmergencyRegResult.class); + mEmergencyRegistrationResult = in.readParcelable( + EmergencyRegistrationResult.class.getClassLoader(), + EmergencyRegistrationResult.class); } public static final @NonNull Creator<SelectionAttributes> CREATOR = @@ -413,7 +415,7 @@ public class DomainSelectionService extends Service { private boolean mIsExitedFromAirplaneMode; private @Nullable ImsReasonInfo mImsReasonInfo; private @PreciseDisconnectCauses int mCause; - private @Nullable EmergencyRegResult mEmergencyRegResult; + private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult; /** * Default constructor for Builder. @@ -430,7 +432,7 @@ public class DomainSelectionService extends Service { * @param callId The call identifier. * @return The same instance of the builder. */ - public @NonNull Builder setCallId(@NonNull String callId) { + public @NonNull Builder setCallId(@Nullable String callId) { mCallId = callId; return this; } @@ -441,7 +443,7 @@ public class DomainSelectionService extends Service { * @param address The dialed address. * @return The same instance of the builder. */ - public @NonNull Builder setAddress(@NonNull Uri address) { + public @NonNull Builder setAddress(@Nullable Uri address) { mAddress = address; return this; } @@ -497,7 +499,7 @@ public class DomainSelectionService extends Service { * @param info The reason why the last PS attempt failed. * @return The same instance of the builder. */ - public @NonNull Builder setPsDisconnectCause(@NonNull ImsReasonInfo info) { + public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) { mImsReasonInfo = info; return this; } @@ -519,8 +521,9 @@ public class DomainSelectionService extends Service { * @param regResult The current registration result for emergency services. * @return The same instance of the builder. */ - public @NonNull Builder setEmergencyRegResult(@NonNull EmergencyRegResult regResult) { - mEmergencyRegResult = regResult; + public @NonNull Builder setEmergencyRegistrationResult( + @Nullable EmergencyRegistrationResult regResult) { + mEmergencyRegistrationResult = regResult; return this; } @@ -532,7 +535,7 @@ public class DomainSelectionService extends Service { return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress, mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode, mImsReasonInfo, - mCause, mEmergencyRegResult); + mCause, mEmergencyRegistrationResult); } } } @@ -697,7 +700,7 @@ public class DomainSelectionService extends Service { public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks, @EmergencyScanType int scanType, boolean resetScan, @NonNull CancellationSignal signal, - @NonNull Consumer<EmergencyRegResult> consumer) { + @NonNull Consumer<EmergencyRegistrationResult> consumer) { try { if (signal != null) signal.setOnCancelListener(this); mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor); @@ -721,17 +724,18 @@ public class DomainSelectionService extends Service { private class IWwanSelectorResultCallbackAdapter extends IWwanSelectorResultCallback.Stub { - private final @NonNull Consumer<EmergencyRegResult> mConsumer; + private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer; private final @NonNull Executor mExecutor; - IWwanSelectorResultCallbackAdapter(@NonNull Consumer<EmergencyRegResult> consumer, + IWwanSelectorResultCallbackAdapter( + @NonNull Consumer<EmergencyRegistrationResult> consumer, @NonNull Executor executor) { mConsumer = consumer; mExecutor = executor; } @Override - public void onComplete(@NonNull EmergencyRegResult result) { + public void onComplete(@NonNull EmergencyRegistrationResult result) { if (mConsumer == null) return; executeMethodAsyncNoException(mExecutor, @@ -759,9 +763,8 @@ public class DomainSelectionService extends Service { * @param attr Required to determine the domain. * @param callback The callback instance being registered. */ - public void onDomainSelection(@NonNull SelectionAttributes attr, - @NonNull TransportSelectorCallback callback) { - } + public abstract void onDomainSelection(@NonNull SelectionAttributes attr, + @NonNull TransportSelectorCallback callback); /** * Notifies the change in {@link ServiceState} for a specific logical slot index. @@ -836,7 +839,7 @@ public class DomainSelectionService extends Service { /** @hide */ @Override - public @Nullable IBinder onBind(@Nullable Intent intent) { + public final @Nullable IBinder onBind(@Nullable Intent intent) { if (intent == null) return null; if (SERVICE_INTERFACE.equals(intent.getAction())) { Log.i(LOG_TAG, "DomainSelectionService Bound."); @@ -863,7 +866,7 @@ public class DomainSelectionService extends Service { * @return {@link Executor} instance. * @hide */ - public @NonNull Executor getCachedExecutor() { + public final @NonNull Executor getCachedExecutor() { synchronized (mExecutorLock) { if (mExecutor == null) { Executor e = onCreateExecutor(); diff --git a/telephony/java/android/telephony/EmergencyRegResult.aidl b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl index f7229621c0c1..3056031d6f03 100644 --- a/telephony/java/android/telephony/EmergencyRegResult.aidl +++ b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl @@ -16,4 +16,4 @@ package android.telephony; -parcelable EmergencyRegResult; +parcelable EmergencyRegistrationResult; diff --git a/telephony/java/android/telephony/EmergencyRegResult.java b/telephony/java/android/telephony/EmergencyRegistrationResult.java index 15579be2d786..7041f5b3b556 100644 --- a/telephony/java/android/telephony/EmergencyRegResult.java +++ b/telephony/java/android/telephony/EmergencyRegistrationResult.java @@ -35,7 +35,7 @@ import java.util.Objects; */ @SystemApi @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) -public final class EmergencyRegResult implements Parcelable { +public final class EmergencyRegistrationResult implements Parcelable { /** * Indicates the cellular network type of the acquired system. @@ -101,7 +101,7 @@ public final class EmergencyRegResult implements Parcelable { * @param iso The ISO-3166-1 alpha-2 country code equivalent, empty string if unknown. * @hide */ - public EmergencyRegResult( + public EmergencyRegistrationResult( @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork, @NetworkRegistrationInfo.RegistrationState int regState, @NetworkRegistrationInfo.Domain int domain, @@ -125,7 +125,7 @@ public final class EmergencyRegResult implements Parcelable { * @param s Source emergency scan result * @hide */ - public EmergencyRegResult(@NonNull EmergencyRegResult s) { + public EmergencyRegistrationResult(@NonNull EmergencyRegistrationResult s) { mAccessNetworkType = s.mAccessNetworkType; mRegState = s.mRegState; mDomain = s.mDomain; @@ -139,9 +139,9 @@ public final class EmergencyRegResult implements Parcelable { } /** - * Construct a EmergencyRegResult object from the given parcel. + * Construct a EmergencyRegistrationResult object from the given parcel. */ - private EmergencyRegResult(@NonNull Parcel in) { + private EmergencyRegistrationResult(@NonNull Parcel in) { readFromParcel(in); } @@ -258,7 +258,7 @@ public final class EmergencyRegResult implements Parcelable { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - EmergencyRegResult that = (EmergencyRegResult) o; + EmergencyRegistrationResult that = (EmergencyRegistrationResult) o; return mAccessNetworkType == that.mAccessNetworkType && mRegState == that.mRegState && mDomain == that.mDomain @@ -311,16 +311,16 @@ public final class EmergencyRegResult implements Parcelable { mCountryIso = in.readString8(); } - public static final @NonNull Creator<EmergencyRegResult> CREATOR = - new Creator<EmergencyRegResult>() { - @Override - public EmergencyRegResult createFromParcel(@NonNull Parcel in) { - return new EmergencyRegResult(in); - } - - @Override - public EmergencyRegResult[] newArray(int size) { - return new EmergencyRegResult[size]; - } - }; + public static final @NonNull Creator<EmergencyRegistrationResult> CREATOR = + new Creator<EmergencyRegistrationResult>() { + @Override + public EmergencyRegistrationResult createFromParcel(@NonNull Parcel in) { + return new EmergencyRegistrationResult(in); + } + + @Override + public EmergencyRegistrationResult[] newArray(int size) { + return new EmergencyRegistrationResult[size]; + } + }; } diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index 64b3c0a203e3..3d3c2e8a9230 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -37,6 +37,7 @@ import java.util.Objects; * Contains information about the modem's activity. May be useful for power stats reporting. * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass @SystemApi public final class ModemActivityInfo implements Parcelable { private static final int TX_POWER_LEVELS = 5; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 85a85c6dfadb..db167c0592df 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -63,6 +63,7 @@ import java.util.stream.Collectors; * For historical reasons this class is not declared as final; however, * it should be treated as though it were final. */ +@android.ravenwood.annotation.RavenwoodKeepPartialClass public class ServiceState implements Parcelable { static final String LOG_TAG = "PHONE"; @@ -1140,6 +1141,7 @@ public class ServiceState implements Parcelable { * * @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static @NonNull String frequencyRangeToString(@FrequencyRange int range) { switch (range) { case FREQUENCY_RANGE_UNKNOWN: return "UNKNOWN"; diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java index ea83815c146d..b900af3a986b 100644 --- a/telephony/java/android/telephony/WwanSelectorCallback.java +++ b/telephony/java/android/telephony/WwanSelectorCallback.java @@ -48,7 +48,8 @@ public interface WwanSelectorCallback { */ void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks, @EmergencyScanType int scanType, boolean resetScan, - @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegResult> consumer); + @NonNull CancellationSignal signal, + @NonNull Consumer<EmergencyRegistrationResult> consumer); /** * Notifies the FW that the domain has been selected. After this method is called, diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 2c141cf4a1f4..4a6111444f31 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -502,7 +502,6 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. - * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @@ -526,11 +525,14 @@ public final class SatelliteManager { telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(), attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback); } else { - throw new IllegalStateException("telephony service is null."); + Rlog.e(TAG, "requestEnabled() invalid telephony"); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - Rlog.e(TAG, "requestSatelliteEnabled() RemoteException: ", ex); - ex.rethrowAsRuntimeException(); + Rlog.e(TAG, "requestEnabled() exception: ", ex); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -580,12 +582,14 @@ public final class SatelliteManager { }; telephony.requestIsSatelliteEnabled(mSubId, receiver); } else { + loge("requestIsEnabled() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestIsSatelliteEnabled() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("requestIsEnabled() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -635,12 +639,14 @@ public final class SatelliteManager { }; telephony.requestIsDemoModeEnabled(mSubId, receiver); } else { + loge("requestIsDemoModeEnabled() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { loge("requestIsDemoModeEnabled() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -747,12 +753,14 @@ public final class SatelliteManager { }; telephony.requestIsSatelliteSupported(mSubId, receiver); } else { + loge("requestIsSupported() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestIsSatelliteSupported() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("requestIsSupported() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -802,12 +810,14 @@ public final class SatelliteManager { }; telephony.requestSatelliteCapabilities(mSubId, receiver); } else { + loge("requestCapabilities() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestSatelliteCapabilities() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("requestCapabilities() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -1083,12 +1093,14 @@ public final class SatelliteManager { telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback, internalCallback); } else { + loge("startTransmissionUpdates() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("startSatelliteTransmissionUpdates() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("startTransmissionUpdates() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1138,12 +1150,14 @@ public final class SatelliteManager { () -> resultListener.accept(SATELLITE_RESULT_INVALID_ARGUMENTS))); } } else { + loge("stopTransmissionUpdates() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("stopSatelliteTransmissionUpdates() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("stopTransmissionUpdates() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1161,7 +1175,6 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. - * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @@ -1188,12 +1201,14 @@ public final class SatelliteManager { cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData, errorCallback); } else { + loge("provisionService() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("provisionSatelliteService() RemoteException=" + ex); - ex.rethrowAsRuntimeException(); + loge("provisionService() RemoteException=" + ex); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } if (cancellationSignal != null) { cancellationSignal.setRemote(cancelRemote); @@ -1237,12 +1252,14 @@ public final class SatelliteManager { }; telephony.deprovisionSatelliteService(mSubId, token, errorCallback); } else { + loge("deprovisionService() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("deprovisionSatelliteService() RemoteException=" + ex); - ex.rethrowAsRuntimeException(); + loge("deprovisionService() RemoteException ex=" + ex); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1284,7 +1301,7 @@ public final class SatelliteManager { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("registerForSatelliteProvisionStateChanged() RemoteException: " + ex); + loge("registerForProvisionStateChanged() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } return SATELLITE_RESULT_REQUEST_FAILED; @@ -1371,12 +1388,14 @@ public final class SatelliteManager { }; telephony.requestIsSatelliteProvisioned(mSubId, receiver); } else { + loge("requestIsProvisioned() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestIsSatelliteProvisioned() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + loge("requestIsProvisioned() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -1416,7 +1435,7 @@ public final class SatelliteManager { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("registerForSatelliteModemStateChanged() RemoteException:" + ex); + loge("registerForModemStateChanged() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } return SATELLITE_RESULT_REQUEST_FAILED; @@ -1585,12 +1604,14 @@ public final class SatelliteManager { }; telephony.pollPendingDatagrams(mSubId, internalCallback); } else { + loge("pollPendingDatagrams() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { loge("pollPendingDatagrams() RemoteException:" + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1642,12 +1663,14 @@ public final class SatelliteManager { telephony.sendDatagram(mSubId, datagramType, datagram, needFullScreenPointingUI, internalCallback); } else { + loge("sendDatagram() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { loge("sendDatagram() RemoteException:" + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1697,16 +1720,16 @@ public final class SatelliteManager { } } }; - telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, - receiver); + telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, receiver); } else { + loge("requestIsCommunicationAllowedForCurrentLocation() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " - + ex); - ex.rethrowAsRuntimeException(); + loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -1757,12 +1780,14 @@ public final class SatelliteManager { }; telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver); } else { + loge("requestTimeForNextSatelliteVisibility() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { loge("requestTimeForNextSatelliteVisibility() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -1789,7 +1814,7 @@ public final class SatelliteManager { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("informDeviceAlignedToSatellite() RemoteException:" + ex); + loge("setDeviceAlignedWithSatellite() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1899,12 +1924,14 @@ public final class SatelliteManager { }; telephony.addAttachRestrictionForCarrier(subId, reason, errorCallback); } else { + loge("addAttachRestrictionForCarrier() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { loge("addAttachRestrictionForCarrier() RemoteException:" + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -1942,12 +1969,14 @@ public final class SatelliteManager { }; telephony.removeAttachRestrictionForCarrier(subId, reason, errorCallback); } else { + loge("removeAttachRestrictionForCarrier() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { loge("removeAttachRestrictionForCarrier() RemoteException:" + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } @@ -2008,10 +2037,7 @@ public final class SatelliteManager { * The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no * signal strength data available. * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a - * {@link SatelliteException} with the {@link SatelliteResult}, or return a - * {@link IllegalStateException} if the Telephony process is not currently available or - * satellite is not supported, or return a {@link RuntimeException} when remote procedure call - * has failed. + * {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. */ @@ -2049,12 +2075,14 @@ public final class SatelliteManager { }; telephony.requestNtnSignalStrength(mSubId, receiver); } else { + loge("requestNtnSignalStrength() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { loge("requestNtnSignalStrength() RemoteException: " + ex); - ex.rethrowAsRuntimeException(); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } @@ -2256,14 +2284,11 @@ public final class SatelliteManager { return new ArrayList<>(); } - private static ITelephony getITelephony() { + @Nullable private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer .getTelephonyServiceManager() .getTelephonyServiceRegisterer() .get()); - if (binder == null) { - throw new RuntimeException("Could not find Telephony Service."); - } return binder; } diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl index 0d61fcbb266e..091974a30184 100644 --- a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl +++ b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl @@ -16,8 +16,8 @@ package com.android.internal.telephony; -import android.telephony.EmergencyRegResult; +import android.telephony.EmergencyRegistrationResult; oneway interface IWwanSelectorResultCallback { - void onComplete(in EmergencyRegResult result); + void onComplete(in EmergencyRegistrationResult result); } diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index d7fa124623ce..755636aef7ed 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -70,6 +70,7 @@ import org.mockito.stubbing.Answer; import java.io.File; import java.io.FileOutputStream; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -107,10 +108,13 @@ public class PackageWatchdogTest { private ConnectivityModuleConnector mConnectivityModuleConnector; @Mock private PackageManager mMockPackageManager; + // Mock only sysprop apis + private PackageWatchdog.BootThreshold mSpyBootThreshold; @Captor private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor; private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; + private HashMap<String, String> mCrashRecoveryPropertiesMap; private boolean retry(Supplier<Boolean> supplier) throws Exception { for (int i = 0; i < RETRY_MAX_COUNT; ++i) { @@ -1416,6 +1420,8 @@ public class PackageWatchdogTest { PackageWatchdog watchdog = new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller, mConnectivityModuleConnector, mTestClock); + mockCrashRecoveryProperties(watchdog); + // Verify controller is not automatically started assertThat(controller.mIsEnabled).isFalse(); if (withPackagesReady) { @@ -1432,6 +1438,71 @@ public class PackageWatchdogTest { return watchdog; } + // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions + private void mockCrashRecoveryProperties(PackageWatchdog watchdog) { + try { + mSpyBootThreshold = spy(watchdog.new BootThreshold( + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS)); + mCrashRecoveryPropertiesMap = new HashMap<>(); + + doAnswer((Answer<Integer>) invocationOnMock -> { + String storedValue = mCrashRecoveryPropertiesMap + .getOrDefault("crashrecovery.rescue_boot_count", "0"); + return Integer.parseInt(storedValue); + }).when(mSpyBootThreshold).getCount(); + doAnswer((Answer<Void>) invocationOnMock -> { + int count = invocationOnMock.getArgument(0); + mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count", + Integer.toString(count)); + return null; + }).when(mSpyBootThreshold).setCount(anyInt()); + + doAnswer((Answer<Integer>) invocationOnMock -> { + String storedValue = mCrashRecoveryPropertiesMap + .getOrDefault("crashrecovery.boot_mitigation_count", "0"); + return Integer.parseInt(storedValue); + }).when(mSpyBootThreshold).getMitigationCount(); + doAnswer((Answer<Void>) invocationOnMock -> { + int count = invocationOnMock.getArgument(0); + mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count", + Integer.toString(count)); + return null; + }).when(mSpyBootThreshold).setMitigationCount(anyInt()); + + doAnswer((Answer<Long>) invocationOnMock -> { + String storedValue = mCrashRecoveryPropertiesMap + .getOrDefault("crashrecovery.rescue_boot_start", "0"); + return Long.parseLong(storedValue); + }).when(mSpyBootThreshold).getStart(); + doAnswer((Answer<Void>) invocationOnMock -> { + long count = invocationOnMock.getArgument(0); + mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start", + Long.toString(count)); + return null; + }).when(mSpyBootThreshold).setStart(anyLong()); + + doAnswer((Answer<Long>) invocationOnMock -> { + String storedValue = mCrashRecoveryPropertiesMap + .getOrDefault("crashrecovery.boot_mitigation_start", "0"); + return Long.parseLong(storedValue); + }).when(mSpyBootThreshold).getMitigationStart(); + doAnswer((Answer<Void>) invocationOnMock -> { + long count = invocationOnMock.getArgument(0); + mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start", + Long.toString(count)); + return null; + }).when(mSpyBootThreshold).setMitigationStart(anyLong()); + + Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold"); + mBootThresholdField.setAccessible(true); + mBootThresholdField.set(watchdog, mSpyBootThreshold); + } catch (Exception e) { + // tests will fail, just printing the error + System.out.println("Error detected while spying BootThreshold" + e.getMessage()); + } + } + private static class TestObserver implements PackageHealthObserver { private final String mName; private int mImpact; |