diff options
219 files changed, 8710 insertions, 1099 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index ddcc74696d94..13ecd25d429a 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -3,6 +3,7 @@ package com.android.server.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.app.ActivityManager.ProcessState; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageStatsManager.ForcedReasons; import android.app.usage.UsageStatsManager.StandbyBuckets; @@ -223,4 +224,12 @@ public interface AppStandbyInternal { * a broadcast. */ long getBroadcastResponseWindowDurationMs(); + + /** + * Returns the process state threshold that should be used for deciding whether or not an app + * is in the background in the context of recording broadcast response stats. Apps whose + * process state is higher than this threshold state should be considered to be in background. + */ + @ProcessState + int getBroadcastResponseFgThresholdState(); } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 050e3df5b5e1..b843dcaaf680 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -356,6 +356,14 @@ public class AppStandbyController ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS; /** + * Process state threshold that is used for deciding whether or not an app is in the background + * in the context of recording broadcast response stats. Apps whose process state is higher + * than this threshold state will be considered to be in background. + */ + volatile int mBroadcastResponseFgThresholdState = + ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE; + + /** * Whether we should allow apps into the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. * If false, any attempts to put an app into the bucket will put the app into the @@ -1788,6 +1796,11 @@ public class AppStandbyController } @Override + public int getBroadcastResponseFgThresholdState() { + return mBroadcastResponseFgThresholdState; + } + + @Override public void flushToDisk() { synchronized (mAppIdleLock) { mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime()); @@ -2058,6 +2071,10 @@ public class AppStandbyController TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw); pw.println(); + pw.print(" mBroadcastResponseFgThresholdState="); + pw.print(ActivityManager.procStateToString(mBroadcastResponseFgThresholdState)); + pw.println(); + pw.println(); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); pw.print(" mAllowRestrictedBucket="); @@ -2491,6 +2508,8 @@ public class AppStandbyController }; private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS = "broadcast_response_window_timeout_ms"; + private static final String KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE = + "broadcast_response_fg_threshold_state"; public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = @@ -2522,6 +2541,8 @@ public class AppStandbyController public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS = 2 * ONE_MINUTE; + public static final int DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE = + ActivityManager.PROCESS_STATE_TOP; ConstantsObserver(Handler handler) { super(handler); @@ -2644,6 +2665,11 @@ public class AppStandbyController KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS, DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS); break; + case KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE: + mBroadcastResponseFgThresholdState = properties.getInt( + KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE, + DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE); + break; default: if (!timeThresholdsUpdated && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD) diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index d963e68d80ec..b4edd39894f9 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -61,6 +61,9 @@ java_library { "test_com.android.media", ], min_sdk_version: "29", + lint: { + strict_updatability_linting: true, + }, visibility: [ "//frameworks/av/apex:__subpackages__", "//frameworks/base", // For framework-all diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp index 2714809e0c3d..834e5cbbd330 100644 --- a/apex/media/service/Android.bp +++ b/apex/media/service/Android.bp @@ -47,6 +47,9 @@ java_sdk_library { jarjar_rules: "jarjar_rules.txt", sdk_version: "system_server_current", min_sdk_version: "29", // TODO: We may need to bump this at some point. + lint: { + strict_updatability_linting: true, + }, apex_available: [ "com.android.media", ], diff --git a/core/api/current.txt b/core/api/current.txt index e2325b05b5c8..74594d07a8b8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -326,6 +326,9 @@ package android { field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowClickWhenDisabled = 16844312; // 0x1010618 field public static final int allowEmbedded = 16843765; // 0x10103f5 + field public static final int allowGameAngleDriver; + field public static final int allowGameDownscaling; + field public static final int allowGameFpsOverride; field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 @@ -729,6 +732,10 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 + field public static final int fromExtendBottom; + field public static final int fromExtendLeft; + field public static final int fromExtendRight; + field public static final int fromExtendTop; field public static final int fromId = 16843850; // 0x101044a field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 @@ -1431,10 +1438,12 @@ package android { field public static final int summaryOn = 16843247; // 0x10101ef field public static final int supportedTypes; field public static final int supportsAssist = 16844016; // 0x10104f0 + field public static final int supportsBatteryGameMode; field public static final int supportsInlineSuggestions = 16844301; // 0x101060d field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f field public static final int supportsMultipleDisplays = 16844182; // 0x1010596 + field public static final int supportsPerformanceGameMode; field public static final int supportsPictureInPicture = 16844023; // 0x10104f7 field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsStylusHandwriting; @@ -1574,6 +1583,10 @@ package android { field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 + field public static final int toExtendBottom; + field public static final int toExtendLeft; + field public static final int toExtendRight; + field public static final int toExtendTop; field public static final int toId = 16843849; // 0x1010449 field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 @@ -3122,6 +3135,11 @@ package android.accessibilityservice { field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd field public static final int GLOBAL_ACTION_BACK = 1; // 0x1 field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf + field public static final int GLOBAL_ACTION_DPAD_CENTER = 20; // 0x14 + field public static final int GLOBAL_ACTION_DPAD_DOWN = 17; // 0x11 + field public static final int GLOBAL_ACTION_DPAD_LEFT = 18; // 0x12 + field public static final int GLOBAL_ACTION_DPAD_RIGHT = 19; // 0x13 + field public static final int GLOBAL_ACTION_DPAD_UP = 16; // 0x10 field public static final int GLOBAL_ACTION_HOME = 2; // 0x2 field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8 @@ -31417,6 +31435,9 @@ package android.os { method @Nullable public byte[] createByteArray(); method @Nullable public char[] createCharArray(); method @Nullable public double[] createDoubleArray(); + method @Nullable public <T> T createFixedArray(@NonNull Class<T>, @NonNull int...); + method @Nullable public <T, S extends android.os.IInterface> T createFixedArray(@NonNull Class<T>, @NonNull java.util.function.Function<android.os.IBinder,S>, @NonNull int...); + method @Nullable public <T, S extends android.os.Parcelable> T createFixedArray(@NonNull Class<T>, @NonNull android.os.Parcelable.Creator<S>, @NonNull int...); method @Nullable public float[] createFloatArray(); method @Nullable public int[] createIntArray(); method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>); @@ -31458,6 +31479,9 @@ package android.os { method public void readException(); method public void readException(int, String); method public android.os.ParcelFileDescriptor readFileDescriptor(); + method public <T> void readFixedArray(@NonNull T); + method public <T, S extends android.os.IInterface> void readFixedArray(@NonNull T, @NonNull java.util.function.Function<android.os.IBinder,S>); + method public <T, S extends android.os.Parcelable> void readFixedArray(@NonNull T, @NonNull android.os.Parcelable.Creator<S>); method public float readFloat(); method public void readFloatArray(@NonNull float[]); method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader); @@ -31518,6 +31542,7 @@ package android.os { method public void writeDoubleArray(@Nullable double[]); method public void writeException(@NonNull Exception); method public void writeFileDescriptor(@NonNull java.io.FileDescriptor); + method public <T> void writeFixedArray(@Nullable T, int, @NonNull int...); method public void writeFloat(float); method public void writeFloatArray(@Nullable float[]); method public void writeInt(int); @@ -41213,6 +41238,7 @@ package android.telephony { field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int"; field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int"; field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool"; + field public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool"; field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool"; field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array"; field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array"; @@ -41297,6 +41323,7 @@ package android.telephony { field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; + field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string"; field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool"; field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool"; @@ -41340,6 +41367,7 @@ package android.telephony { field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string"; field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool"; field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; + field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000"; field public static final int SERVICE_CLASS_NONE = 0; // 0x0 field public static final int SERVICE_CLASS_VOICE = 1; // 0x1 field public static final int USSD_OVER_CS_ONLY = 2; // 0x2 @@ -51660,6 +51688,7 @@ package android.view.accessibility { method public boolean isSelected(); method public boolean isShowingHintText(); method public boolean isTextEntryKey(); + method public boolean isTextSelectable(); method public boolean isVisibleToUser(); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int); @@ -51723,6 +51752,7 @@ package android.view.accessibility { method public void setStateDescription(@Nullable CharSequence); method public void setText(CharSequence); method public void setTextEntryKey(boolean); + method public void setTextSelectable(boolean); method public void setTextSelection(int, int); method public void setTooltipText(@Nullable CharSequence); method public void setTouchDelegateInfo(@NonNull android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index df61a968ffc0..520ca47ffcc7 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -54,6 +54,21 @@ package android.app { method public void onCanceled(@NonNull android.app.PendingIntent); } + public class PropertyInvalidatedCache<Query, Result> { + ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>); + method public final void disableForCurrentProcess(); + method public final void invalidateCache(); + method public static void invalidateCache(int, @NonNull String); + method @Nullable public final Result query(@NonNull Query); + field public static final int MODULE_BLUETOOTH = 2; // 0x2 + } + + public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> { + ctor public PropertyInvalidatedCache.QueryHandler(); + method @Nullable public abstract R apply(@NonNull Q); + method public boolean shouldBypassCache(@NonNull Q); + } + public class StatusBarManager { method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean); } @@ -126,6 +141,7 @@ package android.content.pm { public abstract class PackageManager { method @NonNull public String getPermissionControllerPackageName(); + method @NonNull public String getSupplementalProcessPackageName(); } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b3dd7a76a8cf..fd716f341540 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -76,6 +76,7 @@ package android { field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE"; field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; + field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"; field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP"; field public static final String BRICK = "android.permission.BRICK"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; @@ -191,6 +192,7 @@ package android { field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; + field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"; field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN"; field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN"; field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE"; @@ -1188,6 +1190,15 @@ package android.app.admin { field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE"; } + public static final class DevicePolicyResources.Strings.PermissionController { + field public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE"; + field public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE"; + field public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE"; + field public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE = "PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE"; + field public static final String LOCATION_AUTO_GRANTED_MESSAGE = "PermissionController.LOCATION_AUTO_GRANTED_MESSAGE"; + field public static final String WORK_PROFILE_DEFAULT_APPS_TITLE = "PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE"; + } + public final class DevicePolicyStringResource implements android.os.Parcelable { ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int); method public int describeContents(); @@ -2545,6 +2556,107 @@ package android.app.usage { } +package android.app.wallpapereffectsgeneration { + + public final class CameraAttributes implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public float[] getAnchorPointInOutputUvSpace(); + method @NonNull public float[] getAnchorPointInWorldSpace(); + method public float getCameraOrbitPitchDegrees(); + method public float getCameraOrbitYawDegrees(); + method public float getDollyDistanceInWorldSpace(); + method public float getFrustumFarInWorldSpace(); + method public float getFrustumNearInWorldSpace(); + method public float getVerticalFovDegrees(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CameraAttributes> CREATOR; + } + + public static final class CameraAttributes.Builder { + ctor public CameraAttributes.Builder(@NonNull float[], @NonNull float[]); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes build(); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitPitchDegrees(@FloatRange(from=-90.0F, to=90.0f) float); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitYawDegrees(@FloatRange(from=-180.0F, to=180.0f) float); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setDollyDistanceInWorldSpace(float); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumFarInWorldSpace(@FloatRange(from=0.0f) float); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumNearInWorldSpace(@FloatRange(from=0.0f) float); + method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setVerticalFovDegrees(@FloatRange(from=0.0f, to=180.0f, fromInclusive=false) float); + } + + public final class CinematicEffectRequest implements android.os.Parcelable { + ctor public CinematicEffectRequest(@NonNull String, @NonNull android.graphics.Bitmap); + method public int describeContents(); + method @NonNull public android.graphics.Bitmap getBitmap(); + method @NonNull public String getTaskId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectRequest> CREATOR; + } + + public final class CinematicEffectResponse implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getEndKeyFrame(); + method public int getImageContentType(); + method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getStartKeyFrame(); + method public int getStatusCode(); + method @NonNull public String getTaskId(); + method @NonNull public java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh> getTexturedMeshes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2; // 0x2 + field public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3; // 0x3 + field public static final int CINEMATIC_EFFECT_STATUS_OK = 1; // 0x1 + field public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4; // 0x4 + field public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5; // 0x5 + field public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectResponse> CREATOR; + field public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2; // 0x2 + field public static final int IMAGE_CONTENT_TYPE_OTHER = 3; // 0x3 + field public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1; // 0x1 + field public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0; // 0x0 + } + + public static final class CinematicEffectResponse.Builder { + ctor public CinematicEffectResponse.Builder(int, @NonNull String); + method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse build(); + method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setEndKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes); + method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setImageContentType(int); + method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setStartKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes); + method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setTexturedMeshes(@NonNull java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh>); + } + + public final class TexturedMesh implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.graphics.Bitmap getBitmap(); + method @NonNull public int[] getIndices(); + method @NonNull public int getIndicesLayoutType(); + method @NonNull public float[] getVertices(); + method @NonNull public int getVerticesLayoutType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.TexturedMesh> CREATOR; + field public static final int INDICES_LAYOUT_TRIANGLES = 1; // 0x1 + field public static final int INDICES_LAYOUT_UNDEFINED = 0; // 0x0 + field public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1; // 0x1 + field public static final int VERTICES_LAYOUT_UNDEFINED = 0; // 0x0 + } + + public static final class TexturedMesh.Builder { + ctor public TexturedMesh.Builder(@NonNull android.graphics.Bitmap); + method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh build(); + method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndices(@NonNull int[]); + method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndicesLayoutType(int); + method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVertices(@NonNull float[]); + method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVerticesLayoutType(int); + } + + public final class WallpaperEffectsGenerationManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION) public void generateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager.CinematicEffectListener); + } + + public static interface WallpaperEffectsGenerationManager.CinematicEffectListener { + method public void onCinematicEffectGenerated(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse); + } + +} + package android.apphibernation { public class AppHibernationManager { @@ -2733,6 +2845,7 @@ package android.content { field public static final String UI_TRANSLATION_SERVICE = "ui_translation"; field public static final String UWB_SERVICE = "uwb"; field public static final String VR_SERVICE = "vrmanager"; + field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; @@ -3704,6 +3817,7 @@ package android.hardware.hdmi { method public void sendKeyEvent(int, boolean); method public void sendVendorCommand(int, byte[], boolean); method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener); + method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int); } public static interface HdmiClient.OnDeviceSelectedListener { @@ -10285,6 +10399,8 @@ package android.provider { } public static final class Settings.Secure extends android.provider.Settings.NameValueTable { + method public static int getIntForUser(@NonNull android.content.ContentResolver, @NonNull String, int, int); + method @Nullable public static String getStringForUser(@NonNull android.content.ContentResolver, @NonNull String, int); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, @Nullable String, boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); field @Deprecated public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled"; @@ -10636,6 +10752,8 @@ package android.service.attention { method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback); method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback); + method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback); + method public void onStopProximityUpdates(); field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6 field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3 field public static final int ATTENTION_FAILURE_PREEMPTED = 4; // 0x4 @@ -10643,6 +10761,7 @@ package android.service.attention { field public static final int ATTENTION_FAILURE_UNKNOWN = 2; // 0x2 field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0 field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1 + field public static final double PROXIMITY_UNKNOWN = -1.0; field public static final String SERVICE_INTERFACE = "android.service.attention.AttentionService"; } @@ -10651,6 +10770,10 @@ package android.service.attention { method public void onSuccess(int, long); } + public static final class AttentionService.ProximityCallback { + method public void onProximityUpdate(double); + } + } package android.service.autofill { @@ -11074,6 +11197,7 @@ package android.service.games { method public void onCreate(); method public void onDestroy(); method public void onGameTaskFocusChanged(boolean); + method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame(); method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams); method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback); @@ -11503,6 +11627,7 @@ package android.service.trust { method @Deprecated public final void grantTrust(CharSequence, long, boolean); method public final void grantTrust(CharSequence, long, int); method public final void isEscrowTokenActive(long, android.os.UserHandle); + method public final void lockUser(); method public final android.os.IBinder onBind(android.content.Intent); method public boolean onConfigure(java.util.List<android.os.PersistableBundle>); method public void onDeviceLocked(); @@ -11513,13 +11638,16 @@ package android.service.trust { method public void onEscrowTokenStateReceived(long, int); method public void onTrustTimeout(); method public void onUnlockAttempt(boolean); + method public void onUserRequestedUnlock(); method public final void removeEscrowToken(long, android.os.UserHandle); method public final void revokeTrust(); method public final void setManagingTrust(boolean); method public final void showKeyguardErrorMessage(@NonNull CharSequence); method public final void unlockUserWithToken(long, byte[], android.os.UserHandle); field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2 + field public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 8; // 0x8 field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1 + field public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 4; // 0x4 field public static final String SERVICE_INTERFACE = "android.service.trust.TrustAgentService"; field public static final int TOKEN_STATE_ACTIVE = 1; // 0x1 field public static final int TOKEN_STATE_INACTIVE = 0; // 0x0 @@ -11695,6 +11823,17 @@ package android.service.wallpaper { } +package android.service.wallpapereffectsgeneration { + + public abstract class WallpaperEffectsGenerationService extends android.app.Service { + ctor public WallpaperEffectsGenerationService(); + method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public abstract void onGenerateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest); + method public final void returnCinematicEffectResponse(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse); + } + +} + package android.service.watchdog { public abstract class ExplicitHealthCheckService extends android.app.Service { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 15148a93cbe7..bf57bf7f269f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2,7 +2,6 @@ package android { public static final class Manifest.permission { - field public static final String ACCESS_KEYGUARD_SECURE_STORAGE = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"; field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; @@ -279,6 +278,7 @@ package android.app { } public final class GameManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public boolean isAngleEnabled(@NonNull String); method public void setGameServiceProvider(@Nullable String); } @@ -288,8 +288,8 @@ package android.app { } public class KeyguardManager { - method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean checkLock(int, @Nullable byte[]); - method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]); } public class LocaleManager { @@ -356,6 +356,32 @@ package android.app { ctor public PictureInPictureUiState(boolean); } + public class PropertyInvalidatedCache<Query, Result> { + ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>); + method @NonNull public static String createPropertyName(int, @NonNull String); + method public final void disableForCurrentProcess(); + method public static void disableForTestMode(); + method public final void disableInstance(); + method public final void disableSystemWide(); + method public final void forgetDisableLocal(); + method public boolean getDisabledState(); + method public final void invalidateCache(); + method public static void invalidateCache(int, @NonNull String); + method public final boolean isDisabled(); + method @Nullable public final Result query(@NonNull Query); + method public static void setTestMode(boolean); + method public void testPropertyName(); + field public static final int MODULE_BLUETOOTH = 2; // 0x2 + field public static final int MODULE_SYSTEM = 1; // 0x1 + field public static final int MODULE_TEST = 0; // 0x0 + } + + public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> { + ctor public PropertyInvalidatedCache.QueryHandler(); + method @Nullable public abstract R apply(@NonNull Q); + method public boolean shouldBypassCache(@NonNull Q); + } + public class StatusBarManager { method public void cancelRequestAddTile(@NonNull String); method public void clickNotification(@Nullable String, int, int, boolean); @@ -585,15 +611,6 @@ package android.app.prediction { } -package android.app.trust { - - public class TrustManager { - method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void enableTrustAgentForUserForTest(@NonNull android.content.ComponentName, int); - method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int); - } - -} - package android.app.usage { public class NetworkStatsManager { @@ -784,6 +801,7 @@ package android.content.pm { method @NonNull public String getPermissionControllerPackageName(); method @NonNull public abstract String getServicesSystemSharedLibraryPackageName(); method @NonNull public abstract String getSharedSystemSharedLibraryPackageName(); + method @NonNull public String getSupplementalProcessPackageName(); method @Nullable public String getSystemTextClassifierPackageName(); method @Nullable public String getWellbeingPackageName(); method public void holdLock(android.os.IBinder, int); @@ -2374,14 +2392,6 @@ package android.service.quicksettings { } -package android.service.trust { - - public class TrustAgentService extends android.app.Service { - method public void onUserRequestedUnlock(); - } - -} - package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 6b0aef84f14b..42d2d28ae928 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -571,6 +571,31 @@ public abstract class AccessibilityService extends Service { */ public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; + /** + * Action to trigger dpad up keyevent. + */ + public static final int GLOBAL_ACTION_DPAD_UP = 16; + + /** + * Action to trigger dpad down keyevent. + */ + public static final int GLOBAL_ACTION_DPAD_DOWN = 17; + + /** + * Action to trigger dpad left keyevent. + */ + public static final int GLOBAL_ACTION_DPAD_LEFT = 18; + + /** + * Action to trigger dpad right keyevent. + */ + public static final int GLOBAL_ACTION_DPAD_RIGHT = 19; + + /** + * Action to trigger dpad center keyevent. + */ + public static final int GLOBAL_ACTION_DPAD_CENTER = 20; + private static final String LOG_TAG = "AccessibilityService"; /** @@ -2328,10 +2353,16 @@ public abstract class AccessibilityService extends Service { * @param action The action to perform. * @return Whether the action was successfully performed. * + * Perform actions using ids like the id constants referenced below: * @see #GLOBAL_ACTION_BACK * @see #GLOBAL_ACTION_HOME * @see #GLOBAL_ACTION_NOTIFICATIONS * @see #GLOBAL_ACTION_RECENTS + * @see #GLOBAL_ACTION_DPAD_UP + * @see #GLOBAL_ACTION_DPAD_DOWN + * @see #GLOBAL_ACTION_DPAD_LEFT + * @see #GLOBAL_ACTION_DPAD_RIGHT + * @see #GLOBAL_ACTION_DPAD_CENTER */ public final boolean performGlobalAction(int action) { IAccessibilityServiceConnection connection = diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java index bb2b8d45fd61..735df805b872 100644 --- a/core/java/android/accessibilityservice/TouchInteractionController.java +++ b/core/java/android/accessibilityservice/TouchInteractionController.java @@ -376,7 +376,7 @@ public final class TouchInteractionController { throw new IllegalStateException( "State transitions are not allowed without first adding a callback."); } - if (mState != STATE_TOUCH_INTERACTING) { + if (mState != STATE_TOUCH_INTERACTING && mState != STATE_DRAGGING) { throw new IllegalStateException( "State transitions are not allowed in " + stateToString(mState)); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index b1956effb093..20ffa254a9bf 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -863,6 +863,18 @@ public class ApplicationPackageManager extends PackageManager { } } + /** + * @hide + */ + @Override + public String getSupplementalProcessPackageName() { + try { + return mPM.getSupplementalProcessPackageName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override public boolean addPermission(PermissionInfo info) { return getPermissionManager().addPermission(info, false); diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java index 289b3486b7fe..040399ecb83b 100644 --- a/core/java/android/app/GameManager.java +++ b/core/java/android/app/GameManager.java @@ -181,14 +181,18 @@ public final class GameManager { /** * Returns if ANGLE is enabled for a given package and user ID. * <p> + * ANGLE (Almost Native Graphics Layer Engine) can translate OpenGL ES commands to Vulkan + * commands. Enabling ANGLE may improve the performance and/or reduce the power consumption of + * applications. * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}. * * @hide */ + @TestApi @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) - public @GameMode boolean getAngleEnabled(@NonNull String packageName) { + public @GameMode boolean isAngleEnabled(@NonNull String packageName) { try { - return mService.getAngleEnabled(packageName, mContext.getUserId()); + return mService.isAngleEnabled(packageName, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index 3ea0767661bb..7035ac078334 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -26,7 +26,7 @@ interface IGameManagerService { int getGameMode(String packageName, int userId); void setGameMode(String packageName, int gameMode, int userId); int[] getAvailableGameModes(String packageName); - boolean getAngleEnabled(String packageName, int userId); + boolean isAngleEnabled(String packageName, int userId); void setGameState(String packageName, in GameState gameState, int userId); GameModeInfo getGameModeInfo(String packageName, int userId); void setGameServiceProvider(String packageName); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index ec8d989c61d4..715de14345f7 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -16,7 +16,11 @@ package android.app; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -27,9 +31,10 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastPrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -104,17 +109,21 @@ import java.util.concurrent.atomic.AtomicLong; * <pre> * public class ActivityThread { * ... + * private final PropertyInvalidatedCache.QueryHandler<Integer, Birthday> mBirthdayQuery = + * new PropertyInvalidatedCache.QueryHandler<Integer, Birthday>() { + * {@literal @}Override + * public Birthday apply(Integer) { + * return GetService("birthdayd").getUserBirthday(userId); + * } + * }; * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd"; * private final PropertyInvalidatedCache<Integer, Birthday%> mBirthdayCache = new - * PropertyInvalidatedCache<Integer, Birthday%>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) { - * {@literal @}Override - * protected Birthday recompute(Integer userId) { - * return GetService("birthdayd").getUserBirthday(userId); - * } - * }; + * PropertyInvalidatedCache<Integer, Birthday%>( + * BDAY_CACHE_MAX, MODULE_SYSTEM, "getUserBirthday", mBirthdayQuery); + * * public void disableUserBirthdayCache() { - * mBirthdayCache.disableLocal(); + * mBirthdayCache.disableForCurrentProcess(); * } * public void invalidateUserBirthdayCache() { * mBirthdayCache.invalidateCache(); @@ -221,10 +230,124 @@ import java.util.concurrent.atomic.AtomicLong; * * @param <Query> The class used to index cache entries: must be hashable and comparable * @param <Result> The class holding cache entries; use a boxed primitive if possible - * - * {@hide} + * @hide */ -public abstract class PropertyInvalidatedCache<Query, Result> { +@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) +@TestApi +public class PropertyInvalidatedCache<Query, Result> { + /** + * This is a configuration class that customizes a cache instance. + * @hide + */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public static abstract class QueryHandler<Q,R> { + /** + * Compute a result given a query. The semantics are those of Functor. + */ + public abstract @Nullable R apply(@NonNull Q query); + + /** + * Return true if a query should not use the cache. The default implementation + * always uses the cache. + */ + public boolean shouldBypassCache(@NonNull Q query) { + return false; + } + }; + + /** + * The system properties used by caches should be of the form <prefix>.<module>.<api>, + * where the prefix is "cache_key", the module is one of the constants below, and the + * api is any string. The ability to write the property (which happens during + * invalidation) depends on SELinux rules; these rules are defined against + * <prefix>.<module>. Therefore, the module chosen for a cache property must match + * the permissions granted to the processes that contain the corresponding caches. + * @hide + */ + @IntDef(prefix = { "MODULE_" }, value = { + MODULE_TEST, + MODULE_SYSTEM, + MODULE_BLUETOOTH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Module {} + + /** + * The module used for unit tests and cts tests. It is expected that no process in + * the system has permissions to write properties with this module. + * @hide + */ + @TestApi + public static final int MODULE_TEST = 0; + + /** + * The module used for system server/framework caches. This is not visible outside + * the system processes. + * @hide + */ + @TestApi + public static final int MODULE_SYSTEM = 1; + + /** + * The module used for bluetooth caches. + * @hide + */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public static final int MODULE_BLUETOOTH = 2; + + // A static array mapping module constants to strings. + private final static String[] sModuleNames = + { "test", "system_server", "bluetooth" }; + + /** + * Construct a system property that matches the rules described above. The module is + * one of the permitted values above. The API is a string that is a legal Java simple + * identifier. The api is modified to conform to the system property style guide by + * replacing every upper case letter with an underscore and the lower case equivalent. + * There is no requirement that the apiName be the name of an actual API. + * + * Be aware that SystemProperties has a maximum length which is private to the + * implementation. The current maximum is 92 characters. If this method creates a + * property name that is too long, SystemProperties.set() will fail without a good + * error message. + * @hide + */ + @TestApi + public static @NonNull String createPropertyName(@Module int module, @NonNull String apiName) { + char[] api = apiName.toCharArray(); + int upper = 0; + for (int i = 0; i < api.length; i++) { + if (Character.isUpperCase(api[i])) { + upper++; + } + } + char[] suffix = new char[api.length + upper]; + int j = 0; + for (int i = 0; i < api.length; i++) { + if (Character.isJavaIdentifierPart(api[i])) { + if (Character.isUpperCase(api[i])) { + suffix[j++] = '_'; + suffix[j++] = Character.toLowerCase(api[i]); + } else { + suffix[j++] = api[i]; + } + } else { + throw new IllegalArgumentException("invalid api name"); + } + } + + String moduleName = null; + try { + moduleName = sModuleNames[module]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("invalid module " + module); + } + + return "cache_key." + moduleName + "." + new String(suffix); + } + /** * Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note * that all values cause the cache to be skipped. @@ -335,6 +458,25 @@ public abstract class PropertyInvalidatedCache<Query, Result> { */ private final String mCacheName; + /** + * The function that computes a Result, given a Query. This function is called on a + * cache miss. + */ + private QueryHandler<Query, Result> mComputer; + + /** + * A default function that delegates to the deprecated recompute() method. + */ + private static class DefaultComputer<Query, Result> extends QueryHandler<Query, Result> { + final PropertyInvalidatedCache<Query, Result> mCache; + DefaultComputer(PropertyInvalidatedCache<Query, Result> cache) { + mCache = cache; + } + public Result apply(Query query) { + return mCache.recompute(query); + } + } + @GuardedBy("mLock") private final LinkedHashMap<Query, Result> mCache; @@ -359,8 +501,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * property name. New clients should prefer the constructor that takes an explicit * cache name. * + * TODO(216112648): deprecate this as a public interface, in favor of the four-argument + * constructor. + * * @param maxEntries Maximum number of entries to cache; LRU discard * @param propertyName Name of the system property holding the cache invalidation nonce. + * + * @hide */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) { this(maxEntries, propertyName, propertyName); @@ -369,32 +516,73 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Make a new property invalidated cache. * + * TODO(216112648): deprecate this as a public interface, in favor of the four-argument + * constructor. + * * @param maxEntries Maximum number of entries to cache; LRU discard * @param propertyName Name of the system property holding the cache invalidation nonce * @param cacheName Name of this cache in debug and dumpsys + * @hide */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName, @NonNull String cacheName) { mPropertyName = propertyName; mCacheName = cacheName; mMaxEntries = maxEntries; - mCache = new LinkedHashMap<Query, Result>( + mComputer = new DefaultComputer<>(this); + mCache = createMap(); + registerCache(); + } + + /** + * Make a new property invalidated cache. The key is computed from the module and api + * parameters. + * + * @param maxEntries Maximum number of entries to cache; LRU discard + * @param module The module under which the cache key should be placed. + * @param api The api this cache front-ends. The api must be a Java identifier but + * need not be an actual api. + * @param cacheName Name of this cache in debug and dumpsys + * @param computer The code to compute values that are not in the cache. + * @hide + */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public PropertyInvalidatedCache(int maxEntries, @Module int module, @NonNull String api, + @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) { + mPropertyName = createPropertyName(module, api); + mCacheName = cacheName; + mMaxEntries = maxEntries; + mComputer = computer; + mCache = createMap(); + registerCache(); + } + + // Create a map. This should be called only from the constructor. + private LinkedHashMap<Query, Result> createMap() { + return new LinkedHashMap<Query, Result>( 2 /* start small */, 0.75f /* default load factor */, true /* LRU access order */) { + @GuardedBy("mLock") @Override protected boolean removeEldestEntry(Map.Entry eldest) { final int size = size(); if (size > mHighWaterMark) { mHighWaterMark = size; } - if (size > maxEntries) { + if (size > mMaxEntries) { mMissOverflow++; return true; } return false; } - }; + }; + } + + // Register the map in the global list. If the cache is disabled globally, disable it + // now. + private void registerCache() { synchronized (sCorkLock) { sCaches.put(this, null); if (sDisabledKeys.contains(mCacheName)) { @@ -418,8 +606,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Enable or disable testing. The testing property map is cleared every time this * method is called. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public static void setTestMode(boolean mode) { sTesting = mode; synchronized (sTestingPropertyMap) { @@ -431,13 +620,22 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * Enable testing the specific cache key. Only keys in the map are subject to testing. * There is no method to stop testing a property name. Just disable the test mode. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public static void testPropertyName(@NonNull String name) { + private static void testPropertyName(@NonNull String name) { synchronized (sTestingPropertyMap) { sTestingPropertyMap.put(name, (long) NONCE_UNSET); } } + /** + * Enable testing the specific cache key. Only keys in the map are subject to testing. + * There is no method to stop testing a property name. Just disable the test mode. + * @hide + */ + @TestApi + public void testPropertyName() { + testPropertyName(mPropertyName); + } + // Read the system property associated with the current cache. This method uses the // handle for faster reading. private long getCurrentNonce() { @@ -490,6 +688,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Forget all cached values. + * TODO(216112648) remove this as a public API. Clients should invalidate caches, not clear + * them. + * @hide */ public final void clear() { synchronized (mLock) { @@ -505,22 +706,29 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * Fetch a result from scratch in case it's not in the cache at all. Called unlocked: may * block. If this function returns null, the result of the cache query is null. There is no * "negative cache" in the query: we don't cache null results at all. + * TODO(216112648): deprecate this as a public interface, in favor of an instance of + * QueryHandler. + * @hide */ - public abstract @NonNull Result recompute(@NonNull Query query); + public Result recompute(@NonNull Query query) { + return mComputer.apply(query); + } /** * Return true if the query should bypass the cache. The default behavior is to * always use the cache but the method can be overridden for a specific class. + * TODO(216112648): deprecate this as a public interface, in favor of an instance of + * QueryHandler. + * @hide */ public boolean bypass(@NonNull Query query) { - return false; + return mComputer.shouldBypassCache(query); } /** - * Determines if a pair of responses are considered equal. Used to determine whether a - * cache is inadvertently returning stale results when VERIFY is set to true. Some - * existing clients override this method, but it is now deprecated in favor of a valid - * equals() method on the Result class. + * Determines if a pair of responses are considered equal. Used to determine whether + * a cache is inadvertently returning stale results when VERIFY is set to true. + * @hide */ public boolean resultEquals(Result cachedResult, Result fetchedResult) { // If a service crashes and returns a null result, the cached value remains valid. @@ -541,6 +749,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * the meantime (if the nonce has changed in the meantime, we drop the cache and try the * whole query again), or 3) null, which causes the old value to be removed from the cache * and null to be returned as the result of the cache query. + * @hide */ protected Result refresh(Result oldResult, Query query) { return oldResult; @@ -551,7 +760,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * testing. To disable a cache in normal code, use disableLocal(). * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public final void disableInstance() { synchronized (mLock) { mDisabled = true; @@ -580,9 +789,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * disabled remain disabled (the "disabled" setting is sticky). However, new caches * with this name will not be disabled. It is not an error if the cache name is not * found in the list of disabled caches. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public final void clearDisableLocal() { + @TestApi + public final void forgetDisableLocal() { synchronized (sCorkLock) { sDisabledKeys.remove(mCacheName); } @@ -592,25 +802,43 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * Disable this cache in the current process, and all other caches that use the same * name. This does not affect caches that have a different name but use the same * property. + * TODO(216112648) Remove this in favor of disableForCurrentProcess(). + * @hide */ public final void disableLocal() { + disableForCurrentProcess(); + } + + /** + * Disable this cache in the current process, and all other caches that use the same + * name. This does not affect caches that have a different name but use the same + * property. + * @hide + */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public final void disableForCurrentProcess() { disableLocal(mCacheName); } /** - * Return whether the cache is disabled in this process. + * Return whether a cache instance is disabled. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public final boolean isDisabledLocal() { + @TestApi + public final boolean isDisabled() { return mDisabled || !sEnabled; } /** * Get a value from the cache or recompute it. + * @hide */ - public @NonNull Result query(@NonNull Query query) { + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public final @Nullable Result query(@NonNull Query query) { // Let access to mDisabled race: it's atomic anyway. - long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED; + long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED; if (bypass(query)) { currentNonce = NONCE_BYPASS; } @@ -724,8 +952,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * When multiple caches share a single property value, using an instance method on one of * the cache objects to invalidate all of the cache objects becomes confusing and you should * just use the static version of this function. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public final void disableSystemWide() { disableSystemWide(mPropertyName); } @@ -746,16 +975,33 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Non-static convenience version of invalidateCache() for situations in which only a single * PropertyInvalidatedCache is keyed on a particular property value. + * @hide */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi public final void invalidateCache() { invalidateCache(mPropertyName); } /** + * Invalidate caches in all processes that are keyed for the module and api. + * @hide + */ + @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public static void invalidateCache(@Module int module, @NonNull String api) { + invalidateCache(createPropertyName(module, api)); + } + + /** * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on * {@var name}. This function is synchronous: caches are invalidated upon return. * + * TODO(216112648) make this method private in favor of the two-argument (module, api) + * override. + * * @param name Name of the cache-key property to invalidate + * @hide */ public static void invalidateCache(@NonNull String name) { if (!sEnabled) { @@ -824,6 +1070,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * corkInvalidations() and uncorkInvalidations() must be called in pairs. * * @param name Name of the cache-key property to cork + * @hide */ public static void corkInvalidations(@NonNull String name) { if (!sEnabled) { @@ -871,6 +1118,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * transitioning it to normal operation (unless explicitly disabled system-wide). * * @param name Name of the cache-key property to uncork + * @hide */ public static void uncorkInvalidations(@NonNull String name) { if (!sEnabled) { @@ -916,6 +1164,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * The auto-cork delay is configurable but it should not be too long. The purpose of * the delay is to minimize the number of times a server writes to the system property * when invalidating the cache. One write every 50ms does not hurt system performance. + * @hide */ public static final class AutoCorker { public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50; @@ -1043,6 +1292,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * Return the query as a string, to be used in debug messages. New clients should not * override this, but should instead add the necessary toString() method to the Query * class. + * TODO(216112648) add a method in the QueryHandler and deprecate this API. + * @hide */ protected @NonNull String queryToString(@NonNull Query query) { return Objects.toString(query); @@ -1054,8 +1305,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * process does not have privileges to write SystemProperties. Once disabled it is not * possible to re-enable caching in the current process. If a client wants to * temporarily disable caching, use the corking mechanism. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public static void disableForTestMode() { Log.d(TAG, "disabling all caches in the process"); sEnabled = false; @@ -1064,10 +1316,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Report the disabled status of this cache instance. The return value does not * reflect status of the property key. + * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public boolean getDisabledState() { - return isDisabledLocal(); + return isDisabled(); } /** @@ -1133,7 +1386,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } /** - * Dumps the contents of every cache in the process to the provided ParcelFileDescriptor. + * Dumps contents of every cache in the process to the provided ParcelFileDescriptor. + * @hide */ public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) { ArrayList<PropertyInvalidatedCache> activeCaches; @@ -1174,6 +1428,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Trim memory by clearing all the caches. + * @hide */ public static void onTrimMemory() { for (PropertyInvalidatedCache pic : getActiveCaches()) { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index bbdd7050cb07..79180cbc57fd 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -51,6 +51,8 @@ import android.app.usage.IUsageStatsManager; import android.app.usage.NetworkStatsManager; import android.app.usage.StorageStatsManager; import android.app.usage.UsageStatsManager; +import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager; +import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager; import android.apphibernation.AppHibernationManager; import android.appwidget.AppWidgetManager; import android.bluetooth.BluetoothFrameworkInitializer; @@ -1297,6 +1299,20 @@ public final class SystemServiceRegistry { } }); + registerService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE, + WallpaperEffectsGenerationManager.class, + new CachedServiceFetcher<WallpaperEffectsGenerationManager>() { + @Override + public WallpaperEffectsGenerationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService( + Context.WALLPAPER_EFFECTS_GENERATION_SERVICE); + return b == null ? null : + new WallpaperEffectsGenerationManager( + IWallpaperEffectsGenerationManager.Stub.asInterface(b)); + } + }); + registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() { @Override public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException { diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index cf349b04425a..ac39cb4f1e23 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -93,6 +93,12 @@ import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOC import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.LOCATION_AUTO_GRANTED_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE; import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK; import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE; @@ -117,8 +123,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACT import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_LOCK_DEVICE; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_APPS_WARNING; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_BUG_REPORT_WARNING; -import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_NETWORK_LOGS_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_USAGE_WARNING; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_WORK_DATA_WARNING; import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_WIPE_DEVICE; @@ -500,7 +506,13 @@ public final class DevicePolicyResources { WORK_PROFILE_PRIVACY_POLICY_INFO, CONNECTED_APPS_SEARCH_KEYWORDS, WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS, ACCOUNTS_SEARCH_KEYWORDS, CONTROLLED_BY_ADMIN_SUMMARY, WORK_PROFILE_USER_LABEL, WORK_CATEGORY_HEADER, - PERSONAL_CATEGORY_HEADER + PERSONAL_CATEGORY_HEADER, + + // PermissionController Strings + WORK_PROFILE_DEFAULT_APPS_TITLE, HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE, + BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE, BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, + BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, + LOCATION_AUTO_GRANTED_MESSAGE }) public @interface UpdatableStringId { } @@ -690,11 +702,13 @@ public final class DevicePolicyResources { private static Set<String> buildStringsSet() { Set<String> strings = new HashSet<>(); + strings.addAll(Settings.buildStringsSet()); strings.addAll(Launcher.buildStringsSet()); strings.addAll(SystemUi.buildStringsSet()); strings.addAll(Core.buildStringsSet()); strings.addAll(DocumentsUi.buildStringsSet()); strings.addAll(MediaProvider.buildStringsSet()); + strings.addAll(PermissionController.buildStringsSet()); return strings; } @@ -2854,5 +2868,71 @@ public final class DevicePolicyResources { return strings; } } + + /** + * Class containing the identifiers used to update device management-related system strings + * in the PermissionController module. + */ + public static final class PermissionController { + + private PermissionController() { + } + + private static final String PREFIX = "PermissionController."; + + /** + * Title for settings page to show default apps for work. + */ + public static final String WORK_PROFILE_DEFAULT_APPS_TITLE = + PREFIX + "WORK_PROFILE_DEFAULT_APPS_TITLE"; + + /** + * Summary indicating that a home role holder app is missing work profile support. + */ + public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE = + PREFIX + "HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE"; + + /** + * Summary of a permission switch in Settings when the background access is denied by an + * admin. + */ + public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE = + PREFIX + "BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE"; + + /** + * Summary of a permission switch in Settings when the background access is enabled by + * an admin. + */ + public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = + PREFIX + "BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE"; + + /** + * Summary of a permission switch in Settings when the foreground access is enabled by + * an admin. + */ + public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = + PREFIX + "FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE"; + + /** + * Body of the notification shown to notify the user that the location permission has + * been granted to an app, accepts app name as a param. + */ + public static final String LOCATION_AUTO_GRANTED_MESSAGE = + PREFIX + "LOCATION_AUTO_GRANTED_MESSAGE"; + + /** + * @hide + */ + static Set<String> buildStringsSet() { + Set<String> strings = new HashSet<>(); + strings.add(WORK_PROFILE_DEFAULT_APPS_TITLE); + strings.add(HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE); + strings.add(BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE); + strings.add(BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE); + strings.add(FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE); + strings.add(LOCATION_AUTO_GRANTED_MESSAGE); + return strings; + } + } } } diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index 7956a35c7b3d..edabccf23c2c 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -17,7 +17,6 @@ package android.app.trust; import android.app.trust.ITrustListener; -import android.content.ComponentName; import android.hardware.biometrics.BiometricSourceType; /** @@ -30,7 +29,6 @@ interface ITrustManager { void reportUserRequestedUnlock(int userId); void reportUnlockLockout(int timeoutMs, int userId); void reportEnabledTrustAgentsChanged(int userId); - void enableTrustAgentForUserForTest(in ComponentName componentName, int userId); void registerTrustListener(in ITrustListener trustListener); void unregisterTrustListener(in ITrustListener trustListener); void reportKeyguardShowingChanged(); diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index fba2d3e03769..70b7de0767e4 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -16,14 +16,10 @@ package android.app.trust; -import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; - -import android.annotation.NonNull; +import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; @@ -37,17 +33,9 @@ import java.util.ArrayList; import java.util.List; /** - * Interface to the system service managing trust. - * - * <p>This class is for internal use only. This class is marked {@code @TestApi} to - * enable testing the trust system including {@link android.service.trust.TrustAgentService}. - * Methods which are currently not used in tests are marked @hide. - * - * @see com.android.server.trust.TrustManagerService - * + * See {@link com.android.server.trust.TrustManagerService} * @hide */ -@TestApi @SystemService(Context.TRUST_SERVICE) public class TrustManager { @@ -63,8 +51,7 @@ public class TrustManager { private final ITrustManager mService; private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; - /** @hide */ - public TrustManager(@NonNull IBinder b) { + public TrustManager(IBinder b) { mService = ITrustManager.Stub.asInterface(b); mTrustListeners = new ArrayMap<TrustListener, ITrustListener>(); } @@ -75,10 +62,8 @@ public class TrustManager { * * @param userId The id for the user to be locked/unlocked. * @param locked The value for that user's locked state. - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void setDeviceLockedForUser(int userId, boolean locked) { try { mService.setDeviceLockedForUser(userId, locked); @@ -93,11 +78,8 @@ public class TrustManager { * @param successful if true, the unlock attempt was successful. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - * - * @hide */ @UnsupportedAppUsage - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUnlockAttempt(boolean successful, int userId) { try { mService.reportUnlockAttempt(successful, userId); @@ -111,7 +93,6 @@ public class TrustManager { * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int userId) { try { mService.reportUserRequestedUnlock(userId); @@ -131,10 +112,7 @@ public class TrustManager { * attempt to unlock the device again. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUnlockLockout(int timeoutMs, int userId) { try { mService.reportUnlockLockout(timeoutMs, userId); @@ -147,10 +125,7 @@ public class TrustManager { * Reports that the list of enabled trust agents changed for user {@param userId}. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportEnabledTrustAgentsChanged(int userId) { try { mService.reportEnabledTrustAgentsChanged(userId); @@ -160,33 +135,10 @@ public class TrustManager { } /** - * Enables a trust agent. - * - * <p>The agent is specified by {@code componentName} and must be a subclass of - * {@link android.service.trust.TrustAgentService} and otherwise meet the requirements - * to be a trust agent. - * - * <p>This method can only be used in tests. - * - * @param componentName the trust agent to enable - */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) - public void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) { - try { - mService.enableTrustAgentForUserForTest(componentName, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Reports that the visibility of the keyguard has changed. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportKeyguardShowingChanged() { try { mService.reportKeyguardShowingChanged(); @@ -199,10 +151,7 @@ public class TrustManager { * Registers a listener for trust events. * * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. - * - * @hide */ - @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public void registerTrustListener(final TrustListener trustListener) { try { ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { @@ -243,10 +192,7 @@ public class TrustManager { * Unregisters a listener for trust events. * * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. - * - * @hide */ - @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public void unregisterTrustListener(final TrustListener trustListener) { ITrustListener iTrustListener = mTrustListeners.remove(trustListener); if (iTrustListener != null) { @@ -261,8 +207,6 @@ public class TrustManager { /** * @return whether {@param userId} has enabled and configured trust agents. Ignores short-term * unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}. - * - * @hide */ @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public boolean isTrustUsuallyManaged(int userId) { @@ -279,10 +223,8 @@ public class TrustManager { * can be skipped. * * @param userId - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void unlockedByBiometricForUser(int userId, BiometricSourceType source) { try { mService.unlockedByBiometricForUser(userId, source); @@ -293,10 +235,8 @@ public class TrustManager { /** * Clears authentication by the specified biometric type for all users. - * - * @hide */ - @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) { try { mService.clearAllBiometricRecognized(source, unlockedUser); @@ -324,7 +264,6 @@ public class TrustManager { } }; - /** @hide */ public interface TrustListener { /** diff --git a/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java new file mode 100644 index 000000000000..dfbc7a4c3276 --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Representing the position and other parameters of camera of a single frame. + * + * @hide + */ +@SystemApi +public final class CameraAttributes implements Parcelable { + /** + * The location of the anchor within the 3D scene. + * Expecting 3 floats representing the x, y, z coordinates + * of the anchor point. + */ + @NonNull + private float[] mAnchorPointInWorldSpace; + /** + * Where the anchor point should project to in the output image. + * Expecting 2 floats representing the u,v coordinates of the + * anchor point. + */ + @NonNull + private float[] mAnchorPointInOutputUvSpace; + /** + * Specifies the amount of yaw orbit rotation the camera should perform + * around the anchor point in world space. + */ + private float mCameraOrbitYawDegrees; + /** + * Specifies the amount of pitch orbit rotation the camera should perform + * around the anchor point in world space. + */ + private float mCameraOrbitPitchDegrees; + /** + * Specifies by how much the camera should be placed towards the anchor + * point in world space, which is also called dolly distance. + */ + private float mDollyDistanceInWorldSpace; + /** + * Specifies the vertical fov degrees of the virtual image. + */ + private float mVerticalFovDegrees; + /** + * The frustum of near plane. + */ + private float mFrustumNearInWorldSpace; + /** + * The frustum of far plane. + */ + private float mFrustumFarInWorldSpace; + + private CameraAttributes(Parcel in) { + this.mCameraOrbitYawDegrees = in.readFloat(); + this.mCameraOrbitPitchDegrees = in.readFloat(); + this.mDollyDistanceInWorldSpace = in.readFloat(); + this.mVerticalFovDegrees = in.readFloat(); + this.mFrustumNearInWorldSpace = in.readFloat(); + this.mFrustumFarInWorldSpace = in.readFloat(); + this.mAnchorPointInWorldSpace = in.createFloatArray(); + this.mAnchorPointInOutputUvSpace = in.createFloatArray(); + } + + private CameraAttributes(float[] anchorPointInWorldSpace, float[] anchorPointInOutputUvSpace, + float cameraOrbitYawDegrees, float cameraOrbitPitchDegrees, + float dollyDistanceInWorldSpace, + float verticalFovDegrees, float frustumNearInWorldSpace, float frustumFarInWorldSpace) { + mAnchorPointInWorldSpace = anchorPointInWorldSpace; + mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace; + mCameraOrbitYawDegrees = cameraOrbitYawDegrees; + mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees; + mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace; + mVerticalFovDegrees = verticalFovDegrees; + mFrustumNearInWorldSpace = frustumNearInWorldSpace; + mFrustumFarInWorldSpace = frustumFarInWorldSpace; + } + + /** + * Get the location of the anchor within the 3D scene. The response float array contains + * 3 floats representing the x, y, z coordinates + */ + @NonNull + public float[] getAnchorPointInWorldSpace() { + return mAnchorPointInWorldSpace; + } + + /** + * Get where the anchor point should project to in the output image. The response float + * array contains 2 floats representing the u,v coordinates of the anchor point. + */ + @NonNull + public float[] getAnchorPointInOutputUvSpace() { + return mAnchorPointInOutputUvSpace; + } + + /** + * Get the camera yaw orbit rotation. + */ + public float getCameraOrbitYawDegrees() { + return mCameraOrbitYawDegrees; + } + + /** + * Get the camera pitch orbit rotation. + */ + public float getCameraOrbitPitchDegrees() { + return mCameraOrbitPitchDegrees; + } + + /** + * Get how many units the camera should be placed towards the anchor point in world space. + */ + public float getDollyDistanceInWorldSpace() { + return mDollyDistanceInWorldSpace; + } + + /** + * Get the camera vertical fov degrees. + */ + public float getVerticalFovDegrees() { + return mVerticalFovDegrees; + } + + /** + * Get the frustum in near plane. + */ + public float getFrustumNearInWorldSpace() { + return mFrustumNearInWorldSpace; + } + + /** + * Get the frustum in far plane. + */ + public float getFrustumFarInWorldSpace() { + return mFrustumFarInWorldSpace; + } + + @NonNull + public static final Creator<CameraAttributes> CREATOR = new Creator<CameraAttributes>() { + @Override + public CameraAttributes createFromParcel(Parcel in) { + return new CameraAttributes(in); + } + + @Override + public CameraAttributes[] newArray(int size) { + return new CameraAttributes[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeFloat(mCameraOrbitYawDegrees); + out.writeFloat(mCameraOrbitPitchDegrees); + out.writeFloat(mDollyDistanceInWorldSpace); + out.writeFloat(mVerticalFovDegrees); + out.writeFloat(mFrustumNearInWorldSpace); + out.writeFloat(mFrustumFarInWorldSpace); + out.writeFloatArray(mAnchorPointInWorldSpace); + out.writeFloatArray(mAnchorPointInOutputUvSpace); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Builder for {@link CameraAttributes}. + * + * @hide + */ + @SystemApi + public static final class Builder { + @NonNull + private float[] mAnchorPointInWorldSpace; + @NonNull + private float[] mAnchorPointInOutputUvSpace; + private float mCameraOrbitYawDegrees; + private float mCameraOrbitPitchDegrees; + private float mDollyDistanceInWorldSpace; + private float mVerticalFovDegrees; + private float mFrustumNearInWorldSpace; + private float mFrustumFarInWorldSpace; + + /** + * Constructor with anchor point in world space and anchor point in output image + * space. + * + * @param anchorPointInWorldSpace the location of the anchor within the 3D scene. The + * float array contains 3 floats representing the x, y, z coordinates. + * @param anchorPointInOutputUvSpace where the anchor point should project to in the + * output image. The float array contains 2 floats representing the u,v coordinates + * of the anchor point. + * + * @hide + */ + @SystemApi + public Builder(@NonNull float[] anchorPointInWorldSpace, + @NonNull float[] anchorPointInOutputUvSpace) { + mAnchorPointInWorldSpace = anchorPointInWorldSpace; + mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace; + } + + /** + * Sets the camera orbit yaw rotation. + */ + @NonNull + public Builder setCameraOrbitYawDegrees( + @FloatRange(from = -180.0f, to = 180.0f) float cameraOrbitYawDegrees) { + mCameraOrbitYawDegrees = cameraOrbitYawDegrees; + return this; + } + + /** + * Sets the camera orbit pitch rotation. + */ + @NonNull + public Builder setCameraOrbitPitchDegrees( + @FloatRange(from = -90.0f, to = 90.0f) float cameraOrbitPitchDegrees) { + mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees; + return this; + } + + /** + * Sets the camera dolly distance. + */ + @NonNull + public Builder setDollyDistanceInWorldSpace(float dollyDistanceInWorldSpace) { + mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace; + return this; + } + + /** + * Sets the camera vertical fov degree. + */ + @NonNull + public Builder setVerticalFovDegrees( + @FloatRange(from = 0.0f, to = 180.0f, fromInclusive = false) + float verticalFovDegrees) { + mVerticalFovDegrees = verticalFovDegrees; + return this; + } + + /** + * Sets the frustum in near plane. + */ + @NonNull + public Builder setFrustumNearInWorldSpace( + @FloatRange(from = 0.0f) float frustumNearInWorldSpace) { + mFrustumNearInWorldSpace = frustumNearInWorldSpace; + return this; + } + + /** + * Sets the frustum in far plane. + */ + @NonNull + public Builder setFrustumFarInWorldSpace( + @FloatRange(from = 0.0f) float frustumFarInWorldSpace) { + mFrustumFarInWorldSpace = frustumFarInWorldSpace; + return this; + } + + /** + * Builds a new {@link CameraAttributes} instance. + */ + @NonNull + public CameraAttributes build() { + return new CameraAttributes(mAnchorPointInWorldSpace, + mAnchorPointInOutputUvSpace, + mCameraOrbitYawDegrees, + mCameraOrbitPitchDegrees, + mDollyDistanceInWorldSpace, + mVerticalFovDegrees, + mFrustumNearInWorldSpace, + mFrustumFarInWorldSpace); + } + } +} diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl index cb602d7927ce..234774645956 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl +++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021, The Android Open Source Project + * Copyright (c) 2022, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.net; +package android.app.wallpapereffectsgeneration; -parcelable NetworkStateSnapshot; +parcelable CinematicEffectRequest;
\ No newline at end of file diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java new file mode 100644 index 000000000000..f2e33131baa2 --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A {@link CinematicEffectRequest} is the data class having all the information + * passed to wallpaper effects generation service. + * + * @hide + */ +@SystemApi +public final class CinematicEffectRequest implements Parcelable { + /** + * Unique id of a cienmatic effect generation task. + */ + @NonNull + private String mTaskId; + + /** + * The bitmap to generate cinematic effect from. + */ + @NonNull + private Bitmap mBitmap; + + private CinematicEffectRequest(Parcel in) { + this.mTaskId = in.readString(); + this.mBitmap = Bitmap.CREATOR.createFromParcel(in); + } + + /** + * Constructor with task id and bitmap. + */ + public CinematicEffectRequest(@NonNull String taskId, @NonNull Bitmap bitmap) { + mTaskId = taskId; + mBitmap = bitmap; + } + + /** + * Returns the task id. + */ + @NonNull + public String getTaskId() { + return mTaskId; + } + + /** + * Returns the bitmap of this request. + */ + @NonNull + public Bitmap getBitmap() { + return mBitmap; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) { + return false; + } + CinematicEffectRequest that = (CinematicEffectRequest) o; + return mTaskId.equals(that.mTaskId); + } + + @Override + public int hashCode() { + return Objects.hash(mTaskId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeString(mTaskId); + mBitmap.writeToParcel(out, flags); + } + + @NonNull + public static final Creator<CinematicEffectRequest> CREATOR = + new Creator<CinematicEffectRequest>() { + @Override + public CinematicEffectRequest createFromParcel(Parcel in) { + return new CinematicEffectRequest(in); + } + + @Override + public CinematicEffectRequest[] newArray(int size) { + return new CinematicEffectRequest[size]; + } + }; +} diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl new file mode 100644 index 000000000000..1bd1e1e4bada --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.app.wallpapereffectsgeneration; + + parcelable CinematicEffectResponse;
\ No newline at end of file diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java new file mode 100644 index 000000000000..1254794964dd --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A {@link CinematicEffectResponse} include textured meshes + * and camera attributes of key frames. + * + * @hide + */ +@SystemApi +public final class CinematicEffectResponse implements Parcelable { + /** @hide */ + @IntDef(prefix = {"CINEMATIC_EFFECT_STATUS_"}, + value = {CINEMATIC_EFFECT_STATUS_UNKNOWN, + CINEMATIC_EFFECT_STATUS_OK, + CINEMATIC_EFFECT_STATUS_ERROR, + CINEMATIC_EFFECT_STATUS_NOT_READY, + CINEMATIC_EFFECT_STATUS_PENDING, + CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS}) + @Retention(RetentionPolicy.SOURCE) + public @interface CinematicEffectStatusCode {} + + /** Cinematic effect generation unknown status. */ + public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0; + /** Cinematic effect generation success. */ + public static final int CINEMATIC_EFFECT_STATUS_OK = 1; + /** Cinematic effect generation failure. */ + public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2; + /** Service not ready for cinematic effect generation. */ + public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3; + /** Cienmatic effect generation process is pending. */ + public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4; + /** Too manay requests for server to handle. */ + public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5; + + /** @hide */ + @IntDef(prefix = {"IMAGE_CONTENT_TYPE_"}, + value = {IMAGE_CONTENT_TYPE_UNKNOWN, + IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT, + IMAGE_CONTENT_TYPE_LANDSCAPE, + IMAGE_CONTENT_TYPE_OTHER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImageContentType {} + + /** Image content unknown. */ + public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0; + /** Image content is people portrait. */ + public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1; + /** Image content is landscape. */ + public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2; + /** Image content is doesn't belong to other types. */ + public static final int IMAGE_CONTENT_TYPE_OTHER = 3; + + + @CinematicEffectStatusCode + private int mStatusCode; + + /** The id of the cinematic effect generation task. */ + @NonNull + private String mTaskId; + + @ImageContentType + private int mImageContentType; + + /** The textured mesh required to render cinematic effect. */ + @NonNull + private List<TexturedMesh> mTexturedMeshes; + + /** The start camera position for animation. */ + @Nullable + private CameraAttributes mStartKeyFrame; + + /** The end camera position for animation. */ + @Nullable + private CameraAttributes mEndKeyFrame; + + private CinematicEffectResponse(Parcel in) { + mStatusCode = in.readInt(); + mTaskId = in.readString(); + mImageContentType = in.readInt(); + mTexturedMeshes = new ArrayList<TexturedMesh>(); + in.readTypedList(mTexturedMeshes, TexturedMesh.CREATOR); + mStartKeyFrame = in.readTypedObject(CameraAttributes.CREATOR); + mEndKeyFrame = in.readTypedObject(CameraAttributes.CREATOR); + } + + private CinematicEffectResponse(@CinematicEffectStatusCode int statusCode, + String taskId, + @ImageContentType int imageContentType, + List<TexturedMesh> texturedMeshes, + CameraAttributes startKeyFrame, + CameraAttributes endKeyFrame) { + mStatusCode = statusCode; + mTaskId = taskId; + mImageContentType = imageContentType; + mStartKeyFrame = startKeyFrame; + mEndKeyFrame = endKeyFrame; + mTexturedMeshes = texturedMeshes; + } + + /** Gets the cinematic effect generation status code. */ + @CinematicEffectStatusCode + public int getStatusCode() { + return mStatusCode; + } + + /** Get the task id. */ + @NonNull + public String getTaskId() { + return mTaskId; + } + + /** + * Get the image content type, which briefly classifies what's + * the content of image, like people portrait, landscape etc. + */ + @ImageContentType + public int getImageContentType() { + return mImageContentType; + } + + /** Get the textured meshes. */ + @NonNull + public List<TexturedMesh> getTexturedMeshes() { + return mTexturedMeshes; + } + + /** + * Get the camera attributes (position info and other parameters, see docs of + * {@link CameraAttributes}) of the start key frame on the animation path. + */ + @Nullable + public CameraAttributes getStartKeyFrame() { + return mStartKeyFrame; + } + + /** + * Get the camera attributes (position info and other parameters, see docs of + * {@link CameraAttributes}) of the end key frame on the animation path. + */ + @Nullable + public CameraAttributes getEndKeyFrame() { + return mEndKeyFrame; + } + + @NonNull + public static final Creator<CinematicEffectResponse> CREATOR = + new Creator<CinematicEffectResponse>() { + @Override + public CinematicEffectResponse createFromParcel(Parcel in) { + return new CinematicEffectResponse(in); + } + + @Override + public CinematicEffectResponse[] newArray(int size) { + return new CinematicEffectResponse[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mStatusCode); + out.writeString(mTaskId); + out.writeInt(mImageContentType); + out.writeTypedList(mTexturedMeshes, flags); + out.writeTypedObject(mStartKeyFrame, flags); + out.writeTypedObject(mEndKeyFrame, flags); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) { + return false; + } + CinematicEffectResponse that = (CinematicEffectResponse) o; + return mTaskId.equals(that.mTaskId); + } + + @Override + public int hashCode() { + return Objects.hash(mTaskId); + } + /** + * Builder of {@link CinematicEffectResponse} + * + * @hide + */ + @SystemApi + public static final class Builder { + @CinematicEffectStatusCode + private int mStatusCode; + @NonNull + private String mTaskId; + @ImageContentType + private int mImageContentType; + @NonNull + private List<TexturedMesh> mTexturedMeshes; + @Nullable + private CameraAttributes mStartKeyFrame; + @Nullable + private CameraAttributes mEndKeyFrame; + + /** + * Constructor with task id and status code. + * + * @hide + */ + @SystemApi + public Builder(@CinematicEffectStatusCode int statusCode, @NonNull String taskId) { + mStatusCode = statusCode; + mTaskId = taskId; + } + + /** + * Sets the image content type. + */ + @NonNull + public Builder setImageContentType(@ImageContentType int imageContentType) { + mImageContentType = imageContentType; + return this; + } + + + /** + * Sets the textured meshes. + */ + @NonNull + public Builder setTexturedMeshes(@NonNull List<TexturedMesh> texturedMeshes) { + mTexturedMeshes = texturedMeshes; + return this; + } + + /** + * Sets start key frame. + */ + @NonNull + public Builder setStartKeyFrame(@Nullable CameraAttributes startKeyFrame) { + mStartKeyFrame = startKeyFrame; + return this; + } + + /** + * Sets end key frame. + */ + @NonNull + public Builder setEndKeyFrame(@Nullable CameraAttributes endKeyFrame) { + mEndKeyFrame = endKeyFrame; + return this; + } + + /** + * Builds a {@link CinematicEffectResponse} instance. + */ + @NonNull + public CinematicEffectResponse build() { + if (mTexturedMeshes == null) { + // Place holder because build doesn't allow list to be nullable. + mTexturedMeshes = new ArrayList<>(0); + } + return new CinematicEffectResponse(mStatusCode, mTaskId, mImageContentType, + mTexturedMeshes, mStartKeyFrame, mEndKeyFrame); + } + } +} diff --git a/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl new file mode 100644 index 000000000000..c1a698d6be0e --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.app.wallpapereffectsgeneration.CinematicEffectResponse; + + +/** + * Callback used by system server to notify invoker of {@link WallpaperEffectsGenerationMAnager} + * of the cinematic effect generation result. + * + * @hide + */ +oneway interface ICinematicEffectListener { + void onCinematicEffectGenerated(in CinematicEffectResponse response); +}
\ No newline at end of file diff --git a/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl new file mode 100644 index 000000000000..706a89c043b9 --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.app.wallpapereffectsgeneration.CinematicEffectRequest; +import android.app.wallpapereffectsgeneration.CinematicEffectResponse; +import android.app.wallpapereffectsgeneration.ICinematicEffectListener; + +/** + * Used by {@link android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager} + * to to generate effects. + * + * @hide + */ +oneway interface IWallpaperEffectsGenerationManager { + void generateCinematicEffect(in CinematicEffectRequest request, + in ICinematicEffectListener listener); + + void returnCinematicEffectResponse(in CinematicEffectResponse response); +}
\ No newline at end of file diff --git a/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java new file mode 100644 index 000000000000..630de45e3506 --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The textured mesh representation, including a texture (bitmap) to sample from when rendering, + * and a mesh consisting of primitives such as triangles. The mesh is represented by an indices + * array describing the set of primitives in the mesh, and a vertices array that the indices + * refer to. + * + * @hide + */ +@SystemApi +public final class TexturedMesh implements Parcelable { + /** + * The texture to sample from when rendering mesh. + */ + @NonNull + private Bitmap mBitmap; + + /** + * The set of primitives as pointers into the vertices. + */ + @NonNull + private int[] mIndices; + + /** + * The specific vertices that the indices refer to. + */ + @NonNull + private float[] mVertices; + + /** @hide */ + @IntDef(prefix = {"INDICES_LAYOUT_"}, value = { + INDICES_LAYOUT_UNDEFINED, + INDICES_LAYOUT_TRIANGLES}) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicesLayoutType { + } + + /** Undefined indices layout */ + public static final int INDICES_LAYOUT_UNDEFINED = 0; + /** + * Indices layout is triangle. Vertices are grouped into 3 and each + * group forms a triangle. + */ + public static final int INDICES_LAYOUT_TRIANGLES = 1; + + @IndicesLayoutType + private int mIndicesLayoutType; + + /** @hide */ + @IntDef(prefix = {"VERTICES_LAYOUT_"}, value = { + VERTICES_LAYOUT_UNDEFINED, + VERTICES_LAYOUT_POSITION3_UV2}) + @Retention(RetentionPolicy.SOURCE) + public @interface VerticesLayoutType { + } + + /** + * Undefined vertices layout. + */ + public static final int VERTICES_LAYOUT_UNDEFINED = 0; + /** + * The vertices array uses 5 numbers to represent a point, in the format + * of [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...]. + */ + public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1; + + @VerticesLayoutType + private int mVerticesLayoutType; + + private TexturedMesh(Parcel in) { + this.mIndicesLayoutType = in.readInt(); + this.mVerticesLayoutType = in.readInt(); + this.mBitmap = in.readTypedObject(Bitmap.CREATOR); + Parcel data = Parcel.obtain(); + try { + byte[] bytes = in.readBlob(); + data.unmarshall(bytes, 0, bytes.length); + data.setDataPosition(0); + this.mIndices = data.createIntArray(); + this.mVertices = data.createFloatArray(); + } finally { + data.recycle(); + } + } + + private TexturedMesh(@NonNull Bitmap bitmap, @NonNull int[] indices, + @NonNull float[] vertices, @IndicesLayoutType int indicesLayoutType, + @VerticesLayoutType int verticesLayoutType) { + mBitmap = bitmap; + mIndices = indices; + mVertices = vertices; + mIndicesLayoutType = indicesLayoutType; + mVerticesLayoutType = verticesLayoutType; + } + + /** Get the bitmap, which is the texture to sample from when rendering. */ + @NonNull + public Bitmap getBitmap() { + return mBitmap; + } + + /** + * Get the indices as pointers to the vertices array. Depending on the getIndicesLayoutType(), + * the primitives may have different shapes. For example, with INDICES_LAYOUT_TRIANGLES, + * indices 0, 1, 2 forms a triangle, indices 3, 4, 5 form another triangle. + */ + @NonNull + public int[] getIndices() { + return mIndices; + } + + /** + * Get the vertices that the index array refers to. Depending on the getVerticesLayoutType() + * result, the vertices array can represent different per-vertex coordinates. For example, + * with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of + * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...]. + */ + @NonNull + public float[] getVertices() { + return mVertices; + } + + /** Get the indices layout type. */ + @IndicesLayoutType + @NonNull + public int getIndicesLayoutType() { + return mIndicesLayoutType; + } + + /** Get the indices layout type. */ + @VerticesLayoutType + @NonNull + public int getVerticesLayoutType() { + return mVerticesLayoutType; + } + + @NonNull + public static final Creator<TexturedMesh> CREATOR = new Creator<TexturedMesh>() { + @Override + public TexturedMesh createFromParcel(Parcel in) { + return new TexturedMesh(in); + } + + @Override + public TexturedMesh[] newArray(int size) { + return new TexturedMesh[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mIndicesLayoutType); + out.writeInt(mVerticesLayoutType); + out.writeTypedObject(mBitmap, flags); + + // Indices and vertices can reach 5MB. Write the data as a Blob, + // which will be written to ashmem if too large. + Parcel data = Parcel.obtain(); + try { + data.writeIntArray(mIndices); + data.writeFloatArray(mVertices); + out.writeBlob(data.marshall()); + } finally { + data.recycle(); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * A builder for {@link TexturedMesh} + * + * @hide + */ + @SystemApi + public static final class Builder { + private Bitmap mBitmap; + private int[] mIndices; + private float[] mVertices; + @IndicesLayoutType + private int mIndicesLayoutType; + @VerticesLayoutType + private int mVerticesLayouttype; + + /** + * Constructor with bitmap. + * + * @hide + */ + @SystemApi + public Builder(@NonNull Bitmap bitmap) { + mBitmap = bitmap; + } + + /** + * Set the required indices. The indices should represent the primitives. For example, + * with INDICES_LAYOUT_TRIANGLES, indices 0, 1, 2 forms a triangle, indices 3, 4, 5 + * form another triangle. + */ + @NonNull + public Builder setIndices(@NonNull int[] indices) { + mIndices = indices; + return this; + } + + /** + * Set the required vertices. The vertices array should represent per-vertex coordinates. + * For example, with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of + * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...]. + * + */ + @NonNull + public Builder setVertices(@NonNull float[] vertices) { + mVertices = vertices; + return this; + } + + /** + * Set the required indices layout type. + */ + @NonNull + public Builder setIndicesLayoutType(@IndicesLayoutType int indicesLayoutType) { + mIndicesLayoutType = indicesLayoutType; + return this; + } + + /** + * Set the required vertices layout type. + */ + @NonNull + public Builder setVerticesLayoutType(@VerticesLayoutType int verticesLayoutype) { + mVerticesLayouttype = verticesLayoutype; + return this; + } + + /** Builds a TexturedMesh based on the given parameters. */ + @NonNull + public TexturedMesh build() { + return new TexturedMesh(mBitmap, mIndices, mVertices, mIndicesLayoutType, + mVerticesLayouttype); + } + } +} diff --git a/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java new file mode 100644 index 000000000000..5a1d27daa20b --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.wallpapereffectsgeneration; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; + +import java.util.concurrent.Executor; + +/** + * A {@link WallpaperEffectsGenerationManager} is the class that passes wallpaper effects + * generation requests to wallpaper effect generation service. For example, create a cinematic + * and render a cinematic live wallpaper with the response. + * + * Usage: + * <pre>{@code + * mWallpaperEffectsGenerationManager = + * context.getSystemService(WallpaperEffectsGenerationManager.class); + * mWallpaperEffectsGenerationManager. + * generateCinematicEffect(cinematicEffectRequest, response->{ + * // proceed cinematic effect response. + * }); + * }</pre> + * + * @hide + */ +@SystemApi +@SystemService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE) +public final class WallpaperEffectsGenerationManager { + /** + * Interface for the cinematic effect listener. + */ + public interface CinematicEffectListener { + /** + * Async call when the cinematic effect response is generated. + * Client needs to check the status code of {@link CinematicEffectResponse} + * to determine if the effect generation is successful. + * + * @param response The generated cinematic effect response. + */ + void onCinematicEffectGenerated(@NonNull CinematicEffectResponse response); + } + + private final IWallpaperEffectsGenerationManager mService; + + /** @hide */ + public WallpaperEffectsGenerationManager( + @NonNull IWallpaperEffectsGenerationManager service) { + mService = service; + } + + /** + * Execute a {@link android.app.wallpapereffectsgeneration.CinematicEffectRequest} from + * the given parameters to the wallpaper effects generation service. After the cinematic + * effect response is ready, the given listener is invoked by the system with the response. + * The listener may never receive a callback if unexpected error happened when proceeding + * request. + * + * @param request request to generate cinematic effect. + * @param executor where the listener is invoked. + * @param listener listener invoked when the cinematic effect response is available. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION) + public void generateCinematicEffect(@NonNull CinematicEffectRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull CinematicEffectListener listener) { + try { + mService.generateCinematicEffect(request, + new CinematicEffectListenerWrapper(listener, executor)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static final class CinematicEffectListenerWrapper + extends ICinematicEffectListener.Stub { + @NonNull + private final CinematicEffectListener mListener; + @NonNull + private final Executor mExecutor; + + CinematicEffectListenerWrapper(@NonNull CinematicEffectListener listener, + @NonNull Executor executor) { + mListener = listener; + mExecutor = executor; + } + + @Override + public void onCinematicEffectGenerated(CinematicEffectResponse response) { + mExecutor.execute(() -> mListener.onCinematicEffectGenerated(response)); + } + } +} diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java index 941e9e2ecce5..4e00da1b8c10 100644 --- a/core/java/android/attention/AttentionManagerInternal.java +++ b/core/java/android/attention/AttentionManagerInternal.java @@ -46,6 +46,25 @@ public abstract class AttentionManagerInternal { */ public abstract void cancelAttentionCheck(AttentionCallbackInternal callback); + /** + * Requests the continuous updates of proximity signal via the provided callback, + * until the given callback is unregistered. Currently, AttentionManagerService only + * anticipates one client and updates one client at a time. If a new client wants to + * onboard to receiving Proximity updates, please make a feature request to make proximity + * feature multi-client before depending on this feature. + * + * @param callback a callback that receives the proximity updates + * @return {@code true} if the registration should succeed. + */ + public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback); + + /** + * Requests to stop providing continuous updates until the callback is registered. + * + * @param callback a callback that was used in {@link #onStartProximityUpdates} + */ + public abstract void onStopProximityUpdates(ProximityCallbackInternal callback); + /** Internal interface for attention callback. */ public abstract static class AttentionCallbackInternal { /** @@ -64,4 +83,13 @@ public abstract class AttentionManagerInternal { */ public abstract void onFailure(int error); } + + /** Internal interface for proximity callback. */ + public abstract static class ProximityCallbackInternal { + /** + * @param distance the estimated distance of the user (in meter) + * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive. + */ + public abstract void onProximityUpdate(double distance); + } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a0864d6459d3..52681630dab0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5031,6 +5031,20 @@ public abstract class Context { public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware"; /** + * Used for getting the wallpaper effects generation service. + * + * <p><b>NOTE: </b> this service is optional; callers of + * {@code Context.getSystemServiceName(WALLPAPER_EFFECTS_GENERATION_SERVICE)} should check for + * {@code null}. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = + "wallpaper_effects_generation"; + + /** * Used to access {@link MusicRecognitionManagerService}. * * @hide diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 1c82b38c5007..30aed8bc4de7 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -653,6 +653,7 @@ interface IPackageManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) String getPermissionControllerPackageName(); + String getSupplementalProcessPackageName(); ParceledListSlice getInstantApps(int userId); byte[] getInstantAppCookie(String packageName, int userId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e5c31d7c0a8a..aa647000ee2d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5691,6 +5691,20 @@ public abstract class PackageManager { } /** + * Returns the package name of the component implementing supplemental process service. + * + * @return the package name of the component implementing supplemental process service + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public String getSupplementalProcessPackageName() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Add a new dynamic permission to the system. For this to work, your * package must have defined a permission tree through the * {@link android.R.styleable#AndroidManifestPermissionTree diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java index cee6a5b16a3c..b96e4f88eb93 100644 --- a/core/java/android/hardware/hdmi/HdmiClient.java +++ b/core/java/android/hardware/hdmi/HdmiClient.java @@ -21,6 +21,8 @@ import java.util.concurrent.Executor; public abstract class HdmiClient { private static final String TAG = "HdmiClient"; + private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF; + /* package */ final IHdmiControlService mService; private IHdmiVendorCommandListener mIHdmiVendorCommandListener; @@ -156,11 +158,25 @@ public abstract class HdmiClient { } /** - * Sets a listener used to receive incoming vendor-specific command. + * Sets a listener used to receive incoming vendor-specific command. This listener will only + * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>} + * messages. * * @param listener listener object */ public void setVendorCommandListener(@NonNull VendorCommandListener listener) { + // Set the vendor ID to INVALID_VENDOR_ID. + setVendorCommandListener(listener, UNKNOWN_VENDOR_ID); + } + + /** + * Sets a listener used to receive incoming vendor-specific command. + * + * @param listener listener object + * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with + * this vendorId and all {@code <Vendor Command>} messages. + */ + public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) { if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } @@ -169,7 +185,7 @@ public abstract class HdmiClient { } try { IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener); - mService.addVendorCommandListener(wrappedListener, getDeviceType()); + mService.addVendorCommandListener(wrappedListener, vendorId); mIHdmiVendorCommandListener = wrappedListener; } catch (RemoteException e) { Log.e(TAG, "failed to set vendor command listener: ", e); diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index ec55e121bf74..9235ba15019c 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -564,6 +564,32 @@ public final class HdmiControlManager { @Retention(RetentionPolicy.SOURCE) public @interface TvSendStandbyOnSleep {} + // -- Whether a playback device should act on an incoming {@code <Set Menu Language>} message. + /** + * Confirmation dialog should be shown upon receiving the CEC message. + * + * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE + * @hide + */ + public static final int SET_MENU_LANGUAGE_ENABLED = 1; + /** + * The message should be ignored. + * + * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE + * @hide + */ + public static final int SET_MENU_LANGUAGE_DISABLED = 0; + /** + * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE + * @hide + */ + @IntDef(prefix = { "SET_MENU_LANGUAGE_" }, value = { + SET_MENU_LANGUAGE_ENABLED, + SET_MENU_LANGUAGE_DISABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SetMenuLanguage {} + // -- The RC profile of a TV panel. /** * RC profile none. @@ -818,6 +844,13 @@ public final class HdmiControlManager { public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep"; /** + * Name of a setting deciding whether {@code <Set Menu Language>} message should be + * handled by the framework or ignored. + * + * @hide + */ + public static final String CEC_SETTING_NAME_SET_MENU_LANGUAGE = "set_menu_language"; + /** * Name of a setting representing the RC profile of a TV panel. * * @hide @@ -983,6 +1016,7 @@ public final class HdmiControlManager { CEC_SETTING_NAME_VOLUME_CONTROL_MODE, CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + CEC_SETTING_NAME_SET_MENU_LANGUAGE, CEC_SETTING_NAME_RC_PROFILE_TV, CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java index 16adee9907ed..818554dd8143 100644 --- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java +++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java @@ -221,8 +221,8 @@ public final class HdmiControlServiceWrapper { } @Override - public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { - HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType); + public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) { + HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId); } @Override @@ -481,7 +481,7 @@ public final class HdmiControlServiceWrapper { boolean hasVendorId) {} /** @hide */ - public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {} + public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {} /** @hide */ public void sendStandby(int deviceType, int deviceId) {} diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 6613397c8c0f..35dd9ed5bbcc 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -76,7 +76,7 @@ interface IHdmiControlService { void askRemoteDeviceToBecomeActiveSource(int physicalAddress); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); - void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); + void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId); void sendStandby(int deviceType, int deviceId); void setHdmiRecordListener(IHdmiRecordListener callback); void startOneTouchRecord(int recorderAddress, in byte[] recordSource); diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 27403ec4fe59..e1ffd4a6761d 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -112,7 +112,7 @@ interface IInputManager { oneway void requestPointerCapture(IBinder inputChannelToken, boolean enabled); /** Create an input monitor for gestures. */ - InputMonitor monitorGestureInput(String name, int displayId); + InputMonitor monitorGestureInput(IBinder token, String name, int displayId); // Add a runtime association between the input port and the display port. This overrides any // static associations. diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 979e9dd6a1f6..2fd79cf980c7 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -35,6 +35,7 @@ import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; import android.hardware.lights.LightsRequest; +import android.os.Binder; import android.os.BlockUntrustedTouchesMode; import android.os.Build; import android.os.CombinedVibration; @@ -1211,7 +1212,7 @@ public final class InputManager { */ public InputMonitor monitorGestureInput(String name, int displayId) { try { - return mIm.monitorGestureInput(name, displayId); + return mIm.monitorGestureInput(new Binder(), name, displayId); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 223b8ccf44c8..fc2fbc39dbeb 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -346,7 +346,7 @@ public class InputMethodService extends AbstractInputMethodService { */ @AnyThread public static boolean canImeRenderGesturalNavButtons() { - return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false); + return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true); } /** diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index aa4b83a5c361..0c3514fce76e 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -165,7 +165,7 @@ public class GraphicsEnvironment { private boolean isAngleEnabledByGameMode(Context context, String packageName) { try { final boolean gameModeEnabledAngle = - (mGameManager != null) && mGameManager.getAngleEnabled(packageName); + (mGameManager != null) && mGameManager.isAngleEnabled(packageName); Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle); return gameModeEnabledAngle; } catch (SecurityException e) { diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 3bc3ec83bde5..321b3643b45b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2088,6 +2088,102 @@ public final class Parcel { } /** + * Flatten a homogeneous multi-dimensional array with fixed-size. This delegates to other + * APIs to write a one-dimensional array. Use {@link #readFixedArray(Object)} or + * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal. + * + * @param val The array to be written. + * @param parcelableFlags Contextual flags as per + * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. + * Used only if val is an array of Parcelable objects. + * @param dimensions an array of int representing length of each dimension. The array should be + * sized with the exact size of dimensions. + * + * @see #readFixedArray + * @see #createFixedArray + * @see #writeBooleanArray + * @see #writeByteArray + * @see #writeCharArray + * @see #writeIntArray + * @see #writeLongArray + * @see #writeFloatArray + * @see #writeDoubleArray + * @see #writeBinderArray + * @see #writeInterfaceArray + * @see #writeTypedArray + * @throws BadParcelableException If the array's component type is not supported or if its + * size doesn't match with the given dimensions. + */ + public <T> void writeFixedArray(@Nullable T val, int parcelableFlags, + @NonNull int... dimensions) { + if (val == null) { + writeInt(-1); + return; + } + writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions); + } + + private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index, + int[] dimensions) { + if (index >= dimensions.length) { + throw new BadParcelableException("Array has more dimensions than expected: " + + dimensions.length); + } + + int length = dimensions[index]; + + // val should be an array of length N + if (val == null) { + throw new BadParcelableException("Non-null array shouldn't have a null array."); + } + if (!val.getClass().isArray()) { + throw new BadParcelableException("Not an array: " + val); + } + if (Array.getLength(val) != length) { + throw new BadParcelableException("bad length: expected " + length + ", but got " + + Array.getLength(val)); + } + + // Delegates to other writers if this is a one-dimensional array. + // Otherwise, write component arrays with recursive calls. + + final Class<?> componentType = val.getClass().getComponentType(); + if (!componentType.isArray() && index + 1 != dimensions.length) { + throw new BadParcelableException("Array has fewer dimensions than expected: " + + dimensions.length); + } + if (componentType == boolean.class) { + writeBooleanArray((boolean[]) val); + } else if (componentType == byte.class) { + writeByteArray((byte[]) val); + } else if (componentType == char.class) { + writeCharArray((char[]) val); + } else if (componentType == int.class) { + writeIntArray((int[]) val); + } else if (componentType == long.class) { + writeLongArray((long[]) val); + } else if (componentType == float.class) { + writeFloatArray((float[]) val); + } else if (componentType == double.class) { + writeDoubleArray((double[]) val); + } else if (componentType == IBinder.class) { + writeBinderArray((IBinder[]) val); + } else if (IInterface.class.isAssignableFrom(componentType)) { + writeInterfaceArray((IInterface[]) val); + } else if (Parcelable.class.isAssignableFrom(componentType)) { + writeTypedArray((Parcelable[]) val, parcelableFlags); + } else if (componentType.isArray()) { + writeInt(length); + for (int i = 0; i < length; i++) { + writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1, + dimensions); + } + } else { + throw new BadParcelableException("unknown type for fixed-size array: " + componentType); + } + } + + /** * Flatten a generic object in to a parcel. The given Object value may * currently be one of the following types: * @@ -3788,6 +3884,317 @@ public final class Parcel { } /** + * Read a new multi-dimensional array from a parcel. If you want to read Parcelable or + * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or + * {@link #readFixedArray(Object, Function)}. + * @param val the destination array to hold the read values. + * + * @see #writeTypedArray + * @see #readBooleanArray + * @see #readByteArray + * @see #readCharArray + * @see #readIntArray + * @see #readLongArray + * @see #readFloatArray + * @see #readDoubleArray + * @see #readBinderArray + * @see #readInterfaceArray + * @see #readTypedArray + */ + public <T> void readFixedArray(@NonNull T val) { + Class<?> componentType = val.getClass().getComponentType(); + if (componentType == boolean.class) { + readBooleanArray((boolean[]) val); + } else if (componentType == byte.class) { + readByteArray((byte[]) val); + } else if (componentType == char.class) { + readCharArray((char[]) val); + } else if (componentType == int.class) { + readIntArray((int[]) val); + } else if (componentType == long.class) { + readLongArray((long[]) val); + } else if (componentType == float.class) { + readFloatArray((float[]) val); + } else if (componentType == double.class) { + readDoubleArray((double[]) val); + } else if (componentType == IBinder.class) { + readBinderArray((IBinder[]) val); + } else if (componentType.isArray()) { + int length = readInt(); + if (length != Array.getLength(val)) { + throw new BadParcelableException("Bad length: expected " + Array.getLength(val) + + ", but got " + length); + } + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i)); + } + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + } + + /** + * Read a new multi-dimensional array of typed interfaces from a parcel. + * If you want to read Parcelable values, use + * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use + * {@link #readFixedArray(Object)}. + * @param val the destination array to hold the read values. + */ + public <T, S extends IInterface> void readFixedArray(@NonNull T val, + @NonNull Function<IBinder, S> asInterface) { + Class<?> componentType = val.getClass().getComponentType(); + if (IInterface.class.isAssignableFrom(componentType)) { + readInterfaceArray((S[]) val, asInterface); + } else if (componentType.isArray()) { + int length = readInt(); + if (length != Array.getLength(val)) { + throw new BadParcelableException("Bad length: expected " + Array.getLength(val) + + ", but got " + length); + } + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i), asInterface); + } + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + } + + /** + * Read a new multi-dimensional array of typed parcelables from a parcel. + * If you want to read IInterface values, use + * {@link #readFixedArray(Object, Function)}. For values of other types, use + * {@link #readFixedArray(Object)}. + * @param val the destination array to hold the read values. + */ + public <T, S extends Parcelable> void readFixedArray(@NonNull T val, + @NonNull Parcelable.Creator<S> c) { + Class<?> componentType = val.getClass().getComponentType(); + if (Parcelable.class.isAssignableFrom(componentType)) { + readTypedArray((S[]) val, c); + } else if (componentType.isArray()) { + int length = readInt(); + if (length != Array.getLength(val)) { + throw new BadParcelableException("Bad length: expected " + Array.getLength(val) + + ", but got " + length); + } + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i), c); + } + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + } + + private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) { + if (numDimension <= 0) { + throw new BadParcelableException("Fixed-size array should have dimensions."); + } + + for (int i = 0; i < numDimension; i++) { + if (!cls.isArray()) { + throw new BadParcelableException("Array has fewer dimensions than expected: " + + numDimension); + } + cls = cls.getComponentType(); + } + if (cls.isArray()) { + throw new BadParcelableException("Array has more dimensions than expected: " + + numDimension); + } + } + + /** + * Read and return a new multi-dimensional array from a parcel. Returns null if the + * previously written array object is null. If you want to read Parcelable or + * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or + * {@link #createFixedArray(Class, Function, int[])}. + * @param cls the Class object for the target array type. (e.g. int[][].class) + * @param dimensions an array of int representing length of each dimension. + * + * @see #writeTypedArray + * @see #createBooleanArray + * @see #createByteArray + * @see #createCharArray + * @see #createIntArray + * @see #createLongArray + * @see #createFloatArray + * @see #createDoubleArray + * @see #createBinderArray + * @see #createInterfaceArray + * @see #createTypedArray + */ + @Nullable + public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) { + // Check if type matches with dimensions + // If type is one-dimensional array, delegate to other creators + // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray + + ensureClassHasExpectedDimensions(cls, dimensions.length); + + T val = null; + final Class<?> componentType = cls.getComponentType(); + if (componentType == boolean.class) { + val = (T) createBooleanArray(); + } else if (componentType == byte.class) { + val = (T) createByteArray(); + } else if (componentType == char.class) { + val = (T) createCharArray(); + } else if (componentType == int.class) { + val = (T) createIntArray(); + } else if (componentType == long.class) { + val = (T) createLongArray(); + } else if (componentType == float.class) { + val = (T) createFloatArray(); + } else if (componentType == double.class) { + val = (T) createDoubleArray(); + } else if (componentType == IBinder.class) { + val = (T) createBinderArray(); + } else if (componentType.isArray()) { + int length = readInt(); + if (length < 0) { + return null; + } + if (length != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + + ", but got " + length); + } + + // Create a multi-dimensional array with an innermost component type and dimensions + Class<?> innermost = componentType.getComponentType(); + while (innermost.isArray()) { + innermost = innermost.getComponentType(); + } + val = (T) Array.newInstance(innermost, dimensions); + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i)); + } + return val; + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + + // Check if val is null (which is OK) or has the expected size. + // This check doesn't have to be multi-dimensional because multi-dimensional arrays + // are created with expected dimensions. + if (val != null && Array.getLength(val) != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " + + Array.getLength(val)); + } + return val; + } + + /** + * Read and return a new multi-dimensional array of typed interfaces from a parcel. + * Returns null if the previously written array object is null. If you want to read + * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}. + * For values of other types use {@link #createFixedArray(Class, int[])}. + * @param cls the Class object for the target array type. (e.g. IFoo[][].class) + * @param dimensions an array of int representing length of each dimension. + */ + @Nullable + public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls, + @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) { + // Check if type matches with dimensions + // If type is one-dimensional array, delegate to other creators + // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray + + ensureClassHasExpectedDimensions(cls, dimensions.length); + + T val = null; + final Class<?> componentType = cls.getComponentType(); + if (IInterface.class.isAssignableFrom(componentType)) { + val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n), + asInterface); + } else if (componentType.isArray()) { + int length = readInt(); + if (length < 0) { + return null; + } + if (length != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + + ", but got " + length); + } + + // Create a multi-dimensional array with an innermost component type and dimensions + Class<?> innermost = componentType.getComponentType(); + while (innermost.isArray()) { + innermost = innermost.getComponentType(); + } + val = (T) Array.newInstance(innermost, dimensions); + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i), asInterface); + } + return val; + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + + // Check if val is null (which is OK) or has the expected size. + // This check doesn't have to be multi-dimensional because multi-dimensional arrays + // are created with expected dimensions. + if (val != null && Array.getLength(val) != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " + + Array.getLength(val)); + } + return val; + } + + /** + * Read and return a new multi-dimensional array of typed parcelables from a parcel. + * Returns null if the previously written array object is null. If you want to read + * IInterface values, use {@link #createFixedArray(Class, Function, int[])}. + * For values of other types use {@link #createFixedArray(Class, int[])}. + * @param cls the Class object for the target array type. (e.g. Foo[][].class) + * @param dimensions an array of int representing length of each dimension. + */ + @Nullable + public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls, + @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) { + // Check if type matches with dimensions + // If type is one-dimensional array, delegate to other creators + // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray + + ensureClassHasExpectedDimensions(cls, dimensions.length); + + T val = null; + final Class<?> componentType = cls.getComponentType(); + if (Parcelable.class.isAssignableFrom(componentType)) { + val = (T) createTypedArray(c); + } else if (componentType.isArray()) { + int length = readInt(); + if (length < 0) { + return null; + } + if (length != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + + ", but got " + length); + } + + // Create a multi-dimensional array with an innermost component type and dimensions + Class<?> innermost = componentType.getComponentType(); + while (innermost.isArray()) { + innermost = innermost.getComponentType(); + } + val = (T) Array.newInstance(innermost, dimensions); + for (int i = 0; i < length; i++) { + readFixedArray(Array.get(val, i), c); + } + return val; + } else { + throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); + } + + // Check if val is null (which is OK) or has the expected size. + // This check doesn't have to be multi-dimensional because multi-dimensional arrays + // are created with expected dimensions. + if (val != null && Array.getLength(val) != dimensions[0]) { + throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " + + Array.getLength(val)); + } + return val; + } + + /** * Write a heterogeneous array of Parcelable objects into the Parcel. * Each object in the array is written along with its class name, so * that the correct class can later be instantiated. As a result, this diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 8f5086021530..78f1cb12ded6 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -546,18 +546,22 @@ public abstract class Vibrator { @VibrationEffectSupport public final int areAllEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { - int support = VIBRATION_EFFECT_SUPPORT_YES; - for (int supported : areEffectsSupported(effectIds)) { - if (supported == VIBRATION_EFFECT_SUPPORT_NO) { - return VIBRATION_EFFECT_SUPPORT_NO; - } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) { - support = VIBRATION_EFFECT_SUPPORT_UNKNOWN; + VibratorInfo info = getInfo(); + int allSupported = VIBRATION_EFFECT_SUPPORT_YES; + for (int effectId : effectIds) { + switch (info.isEffectSupported(effectId)) { + case VIBRATION_EFFECT_SUPPORT_NO: + return VIBRATION_EFFECT_SUPPORT_NO; + case VIBRATION_EFFECT_SUPPORT_YES: + continue; + default: // VIBRATION_EFFECT_SUPPORT_UNKNOWN + allSupported = VIBRATION_EFFECT_SUPPORT_UNKNOWN; + break; } } - return support; + return allSupported; } - /** * Query whether the vibrator supports the given primitives. * @@ -598,8 +602,9 @@ public abstract class Vibrator { */ public final boolean areAllPrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { - for (boolean supported : arePrimitivesSupported(primitiveIds)) { - if (!supported) { + VibratorInfo info = getInfo(); + for (int primitiveId : primitiveIds) { + if (!info.isPrimitiveSupported(primitiveId)) { return false; } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c4fe1a44d161..ee6f9c033271 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6094,9 +6094,11 @@ public final class Settings { } /** @hide */ - @UnsupportedAppUsage - public static String getStringForUser(ContentResolver resolver, String name, - int userHandle) { + @SystemApi + @Nullable + @SuppressLint("VisiblySynchronized") + public static String getStringForUser(@NonNull ContentResolver resolver, + @NonNull String name, int userHandle) { if (MOVED_TO_GLOBAL.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure" + " to android.provider.Settings.Global."); @@ -6328,8 +6330,9 @@ public final class Settings { } /** @hide */ - @UnsupportedAppUsage - public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) { + @SystemApi + public static int getIntForUser(@NonNull ContentResolver cr, @NonNull String name, + int def, int userHandle) { String v = getStringForUser(cr, name, userHandle); return parseIntSettingWithDefault(v, def); } diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java index 49ab5db74b87..f5c59b597c3a 100644 --- a/core/java/android/service/attention/AttentionService.java +++ b/core/java/android/service/attention/AttentionService.java @@ -24,11 +24,14 @@ import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; +import android.util.Slog; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.Objects; /** @@ -51,6 +54,7 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi public abstract class AttentionService extends Service { + private static final String LOG_TAG = "AttentionService"; /** * The {@link Intent} that must be declared as handled by the service. To be supported, the * service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE} @@ -80,6 +84,9 @@ public abstract class AttentionService extends Service { /** Camera permission is not granted. */ public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; + /** Users’ proximity is unknown (proximity sensing was inconclusive and is unsupported). */ + public static final double PROXIMITY_UNKNOWN = -1; + /** * Result codes for when attention check was successful. * @@ -118,6 +125,20 @@ public abstract class AttentionService extends Service { Preconditions.checkNotNull(callback); AttentionService.this.onCancelAttentionCheck(new AttentionCallback(callback)); } + + /** {@inheritDoc} */ + @Override + public void onStartProximityUpdates(IProximityCallback callback) { + Objects.requireNonNull(callback); + AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback)); + + } + + /** {@inheritDoc} */ + @Override + public void onStopProximityUpdates() { + AttentionService.this.onStopProximityUpdates(); + } }; @Nullable @@ -143,6 +164,23 @@ public abstract class AttentionService extends Service { */ public abstract void onCancelAttentionCheck(@NonNull AttentionCallback callback); + /** + * Requests the continuous updates of proximity signal via the provided callback, + * until the given callback is unregistered. + * + * @param callback the callback to return the result to + */ + public void onStartProximityUpdates(@NonNull ProximityCallback callback) { + Slog.w(LOG_TAG, "Override this method."); + } + + /** + * Requests to stop providing continuous updates until the callback is registered. + */ + public void onStopProximityUpdates() { + Slog.w(LOG_TAG, "Override this method."); + } + /** Callbacks for AttentionService results. */ public static final class AttentionCallback { @NonNull private final IAttentionCallback mCallback; @@ -174,4 +212,26 @@ public abstract class AttentionService extends Service { } } } + + /** Callbacks for ProximityCallback results. */ + public static final class ProximityCallback { + @NonNull private final WeakReference<IProximityCallback> mCallback; + + private ProximityCallback(@NonNull IProximityCallback callback) { + mCallback = new WeakReference<>(callback); + } + + /** + * @param distance the estimated distance of the user (in meter) + * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive. + * + */ + public void onProximityUpdate(double distance) { + try { + mCallback.get().onProximityUpdate(distance); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } } diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl index 99e79973cfd2..8bb881ba1708 100644 --- a/core/java/android/service/attention/IAttentionService.aidl +++ b/core/java/android/service/attention/IAttentionService.aidl @@ -17,6 +17,7 @@ package android.service.attention; import android.service.attention.IAttentionCallback; +import android.service.attention.IProximityCallback; /** * Interface for a concrete implementation to provide to the AttentionManagerService. @@ -26,4 +27,6 @@ import android.service.attention.IAttentionCallback; oneway interface IAttentionService { void checkAttention(IAttentionCallback callback); void cancelAttentionCheck(IAttentionCallback callback); + void onStartProximityUpdates(IProximityCallback callback); + void onStopProximityUpdates(); }
\ No newline at end of file diff --git a/core/java/android/service/attention/IProximityCallback.aidl b/core/java/android/service/attention/IProximityCallback.aidl new file mode 100644 index 000000000000..9ecf9bc28e84 --- /dev/null +++ b/core/java/android/service/attention/IProximityCallback.aidl @@ -0,0 +1,10 @@ +package android.service.attention; + +/** + * Callback for onStartProximityUpdates request. + * + * @hide + */ +oneway interface IProximityCallback { + void onProximityUpdate(double distance); +} diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java index 9590933cf2da..e33f1801129b 100644 --- a/core/java/android/service/games/GameSession.java +++ b/core/java/android/service/games/GameSession.java @@ -84,6 +84,15 @@ public abstract class GameSession { } @Override + public void onTransientSystemBarVisibilityFromRevealGestureChanged( + boolean visibleDueToGesture) { + Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage( + GameSession::dispatchTransientSystemBarVisibilityFromRevealGestureChanged, + GameSession.this, + visibleDueToGesture)); + } + + @Override public void onTaskFocusChanged(boolean focused) { Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage( GameSession::moveToState, GameSession.this, @@ -109,6 +118,7 @@ public abstract class GameSession { } private LifecycleState mLifecycleState = LifecycleState.INITIALIZED; + private boolean mAreTransientInsetsVisibleDueToGesture = false; private IGameSessionController mGameSessionController; private int mTaskId; private GameSessionRootView mGameSessionRootView; @@ -138,11 +148,23 @@ public abstract class GameSession { } @Hide - void doDestroy() { + private void doDestroy() { mSurfaceControlViewHost.release(); moveToState(LifecycleState.DESTROYED); } + /** @hide */ + @VisibleForTesting + @MainThread + public void dispatchTransientSystemBarVisibilityFromRevealGestureChanged( + boolean visibleDueToGesture) { + boolean didValueChange = mAreTransientInsetsVisibleDueToGesture != visibleDueToGesture; + mAreTransientInsetsVisibleDueToGesture = visibleDueToGesture; + if (didValueChange) { + onTransientSystemBarVisibilityFromRevealGestureChanged(visibleDueToGesture); + } + } + /** * @hide */ @@ -252,7 +274,23 @@ public abstract class GameSession { * * @param focused True if the game task is focused, false if the game task is unfocused. */ - public void onGameTaskFocusChanged(boolean focused) {} + public void onGameTaskFocusChanged(boolean focused) { + } + + /** + * Called when the visibility of the transient system bars changed due to the user performing + * the reveal gesture. The reveal gesture is defined as a swipe to reveal the transient system + * bars that originates from the system bars. + * + * @param visibleDueToGesture if the transient bars triggered by the reveal gesture are visible. + * This is {@code true} when the transient system bars become visible + * due to user performing the reveal gesture. This is {@code false} + * when the transient system bars are hidden or become permanently + * visible. + */ + public void onTransientSystemBarVisibilityFromRevealGestureChanged( + boolean visibleDueToGesture) { + } /** * Sets the task overlay content to an explicit view. This view is placed directly into the game @@ -344,12 +382,14 @@ public abstract class GameSession { /** * Called when taking the screenshot failed. + * * @param statusCode Indicates the reason for failure. */ void onFailure(@ScreenshotFailureStatus int statusCode); /** * Called when taking the screenshot succeeded. + * * @param bitmap The screenshot. */ void onSuccess(@NonNull Bitmap bitmap); diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl index 71da6302b63d..49c36c6a301c 100644 --- a/core/java/android/service/games/IGameSession.aidl +++ b/core/java/android/service/games/IGameSession.aidl @@ -21,5 +21,6 @@ package android.service.games; */ oneway interface IGameSession { void onDestroyed(); + void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean visibleDueToGesture); void onTaskFocusChanged(boolean focused); } diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl index 220e498df8a8..6b11e7463abc 100644 --- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl +++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl @@ -26,6 +26,7 @@ import android.os.UserHandle; oneway interface ITrustAgentServiceCallback { void grantTrust(CharSequence message, long durationMs, int flags); void revokeTrust(); + void lockUser(); void setManagingTrust(boolean managingTrust); void onConfigureCompleted(boolean result, IBinder token); void addEscrowToken(in byte[] token, int userId); diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index 5bd423599f5d..8f6e1e00c3f4 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; @@ -120,16 +119,15 @@ public class TrustAgentService extends Service { * automatically remove trust after some conditions are met (detailed below) with the option for * the agent to renew the trust again later. * - * <p>After this is called, the agent will grant trust until the platform thinks an active user - * is no longer using that trust. For example, if the user dismisses keyguard, the platform will - * remove trust (this does not automatically lock the device). + * <p>After this is called, the agent will grant trust until the platform thinks an active + * user is no longer using that trust. This can happen for any reason as determined by the + * platform. For example, if the user dismisses keyguard, the platform will remove trust; + * since this does not automatically lock the device, this results in the device locking the + * next time the screen turns off. * * <p>When the platform internally removes the agent's trust in this manner, an agent can * re-grant it (via a call to grantTrust) without the user having to unlock the device through * another method (e.g. PIN). This renewable state only persists for a limited time. - * - * TODO(b/213631675): Remove @hide - * @hide */ public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 1 << 2; @@ -140,9 +138,6 @@ public class TrustAgentService extends Service { * Without this flag, the message passed to {@code grantTrust} is only used for debugging * purposes. With the flag, it may be displayed to the user as the reason why the device is * unlocked. - * - * TODO(b/213911325): Remove @hide - * @hide */ public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3; @@ -310,11 +305,7 @@ public class TrustAgentService extends Service { * {@link #grantTrust(CharSequence, long, int)}. * * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE - * - * TODO(b/213631672): Remove @hide and @TestApi - * @hide */ - @TestApi public void onUserRequestedUnlock() { } @@ -626,11 +617,15 @@ public class TrustAgentService extends Service { * * If the user has no auth method specified, then keyguard will still be shown but can be * dismissed normally. - * - * TODO(b/213631675): Implement & make public - * @hide */ public final void lockUser() { + if (mCallback != null) { + try { + mCallback.lockUser(); + } catch (RemoteException e) { + onError("calling lockUser"); + } + } } /** diff --git a/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl new file mode 100644 index 000000000000..ca75d2ed35ed --- /dev/null +++ b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl @@ -0,0 +1,28 @@ + +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.wallpapereffectsgeneration; + +import android.app.wallpapereffectsgeneration.CinematicEffectRequest; +/** + * Interface from the system to WallpaperEffectsGeneration service. + * + * @hide + */ +oneway interface IWallpaperEffectsGenerationService { + void onGenerateCinematicEffect(in CinematicEffectRequest request); +}
\ No newline at end of file diff --git a/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java new file mode 100644 index 000000000000..18b654ec8f04 --- /dev/null +++ b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.wallpapereffectsgeneration; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.Service; +import android.app.wallpapereffectsgeneration.CinematicEffectRequest; +import android.app.wallpapereffectsgeneration.CinematicEffectResponse; +import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.util.Slog; + +/** + * A service for handling wallpaper effects generation tasks. It must implement + * (onGenerateCinematicEffect} method to generate response and call returnCinematicEffectResponse + * to send the response. + * + * <p>To extend this service, you must declare the service in your manifest file with the + * {@link android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION} permission and includes + * an intent filter with the {@link #SERVICE_INTERFACE} action. For example: </p> + * <pre> + * <application> + * <service android:name=".CtsWallpaperEffectsGenerationService" + * android:exported="true" + * android:label="CtsWallpaperEffectsGenerationService" + * android:permission="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"> + * <intent-filter> + * <action android:name="android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService" + /> + * </intent-filter> + * </service> + * <uses-library android:name="android.test.runner"/> + * </application> + * </pre> + * + * @hide + */ +@SystemApi +public abstract class WallpaperEffectsGenerationService extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + * + * <p>The service must also require the + * {@link android.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION} + * permission. + * + * @hide + */ + public static final String SERVICE_INTERFACE = + "android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService"; + private static final boolean DEBUG = false; + private static final String TAG = "WallpaperEffectsGenerationService"; + private Handler mHandler; + private IWallpaperEffectsGenerationManager mService; + + private final IWallpaperEffectsGenerationService mInterface = + new IWallpaperEffectsGenerationService.Stub() { + @Override + public void onGenerateCinematicEffect(CinematicEffectRequest request) { + mHandler.sendMessage( + obtainMessage( + WallpaperEffectsGenerationService::onGenerateCinematicEffect, + WallpaperEffectsGenerationService.this, request)); + } + }; + + /** + * Called when the OS receives a request for generating cinematic effect. On receiving the + * request, it extract cinematic information from the input and call + * {@link #returnCinematicEffectResponse} with the textured mesh + * and metadata wrapped in CinematicEffectResponse. + * + * @param request the cinematic effect request passed from the client. + */ + public abstract void onGenerateCinematicEffect(@NonNull CinematicEffectRequest request); + + /** + * Returns the cinematic effect response. Must be called when cinematic effect + * response is generated and ready to be sent back. Otherwise the response won't be + * returned. + * + * @param response the cinematic effect response returned from service provider. + */ + public final void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) { + try { + mService.returnCinematicEffectResponse(response); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + if (DEBUG) { + Log.d(TAG, "onCreate WallpaperEffectsGenerationService"); + } + mHandler = new Handler(Looper.getMainLooper(), null, true); + IBinder b = ServiceManager.getService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE); + mService = IWallpaperEffectsGenerationManager.Stub.asInterface(b); + } + + @NonNull + @Override + public final IBinder onBind(@NonNull Intent intent) { + if (DEBUG) { + Log.d(TAG, "onBind WallpaperEffectsGenerationService"); + } + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Slog.w(TAG, + "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return null; + } +} diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java index ad1f201ba3c1..8801fe0b47c8 100644 --- a/core/java/android/view/InputMonitor.java +++ b/core/java/android/view/InputMonitor.java @@ -79,13 +79,17 @@ public final class InputMonitor implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -126,7 +130,7 @@ public final class InputMonitor implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -141,7 +145,7 @@ public final class InputMonitor implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ InputMonitor(Parcel in) { + /* package-private */ InputMonitor(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -167,17 +171,21 @@ public final class InputMonitor implements Parcelable { } @Override - public InputMonitor createFromParcel(Parcel in) { + public InputMonitor createFromParcel(@NonNull Parcel in) { return new InputMonitor(in); } }; @DataClass.Generated( - time = 1571177265149L, - codegenVersion = "1.0.7", + time = 1637697281750L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java", inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 0a33d6cd82ea..a31cacfdfd2b 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -710,6 +710,8 @@ public class AccessibilityNodeInfo implements Parcelable { private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000; + private static final int BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE = 0x0800000; + /** * Bits that provide the id of a virtual descendant of a view. */ @@ -2276,6 +2278,38 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets if the node has selectable text. + * + * <p> + * Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must + * also be selectable. But not all UIs will populate this field, so services should consider + * 'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text. + * </p> + * + * @see #isEditable + * @return True if the node has selectable text. + */ + public boolean isTextSelectable() { + return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE); + } + + /** + * Sets if the node has selectable text. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param selectableText True if the node has selectable text, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setTextSelectable(boolean selectableText) { + setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE, selectableText); + } + + /** * Gets if the node is editable. * * @return True if the node is editable, false otherwise. @@ -4327,8 +4361,12 @@ public class AccessibilityNodeInfo implements Parcelable { return "ACTION_CANCEL_DRAG"; case R.id.accessibilityActionDragDrop: return "ACTION_DROP"; - default: + default: { + if (action == R.id.accessibilityActionShowSuggestions) { + return "ACTION_SHOW_SUGGESTIONS"; + } return "ACTION_UNKNOWN"; + } } } @@ -4462,6 +4500,7 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; importantForAccessibility: ").append(isImportantForAccessibility()); builder.append("; visible: ").append(isVisibleToUser()); builder.append("; actions: ").append(mActions); + builder.append("; isTextSelectable: ").append(isTextSelectable()); return builder.toString(); } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index ab749ee284a8..3914a3c963b6 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -865,6 +865,15 @@ public abstract class Animation implements Cloneable { } /** + * @return if a window animation has outsets applied to it. + * + * @hide + */ + public boolean hasExtension() { + return false; + } + + /** * If showBackground is {@code true} and this animation is applied on a window, then the windows * in the animation will animate with the background associated with this window behind them. * @@ -942,6 +951,21 @@ public abstract class Animation implements Cloneable { } /** + * Gets the transformation to apply a specific point in time. Implementations of this method + * should always be kept in sync with getTransformation. + * + * @param normalizedTime time between 0 and 1 where 0 is the start of the animation and 1 the + * end. + * @param outTransformation A transformation object that is provided by the + * caller and will be filled in by the animation. + * @hide + */ + public void getTransformationAt(float normalizedTime, Transformation outTransformation) { + final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); + applyTransformation(interpolatedTime, outTransformation); + } + + /** * Gets the transformation to apply at a specified point in time. Implementations of this * method should always replace the specified Transformation or document they are doing * otherwise. @@ -987,8 +1011,7 @@ public abstract class Animation implements Cloneable { normalizedTime = 1.0f - normalizedTime; } - final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); - applyTransformation(interpolatedTime, outTransformation); + getTransformationAt(normalizedTime, outTransformation); } if (expired) { diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 03c6ca69a592..a2f3544c70ab 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -363,6 +363,26 @@ public class AnimationSet extends Animation { * The transformation of an animation set is the concatenation of all of its * component animations. * + * @see android.view.animation.Animation#getTransformationAt + * @hide + */ + @Override + public void getTransformationAt(float interpolatedTime, Transformation t) { + final Transformation temp = mTempTransformation; + + for (int i = mAnimations.size() - 1; i >= 0; --i) { + final Animation a = mAnimations.get(i); + + temp.clear(); + a.getTransformationAt(interpolatedTime, t); + t.compose(temp); + } + } + + /** + * The transformation of an animation set is the concatenation of all of its + * component animations. + * * @see android.view.animation.Animation#getTransformation */ @Override @@ -517,4 +537,15 @@ public class AnimationSet extends Animation { public boolean willChangeBounds() { return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; } + + /** @hide */ + @Override + public boolean hasExtension() { + for (Animation animation : mAnimations) { + if (animation.hasExtension()) { + return true; + } + } + return false; + } } diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 7ce0f4571f76..7d1dc7660871 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -190,6 +190,8 @@ public class AnimationUtils { anim = new TranslateAnimation(c, attrs); } else if (name.equals("cliprect")) { anim = new ClipRectAnimation(c, attrs); + } else if (name.equals("extend")) { + anim = new ExtendAnimation(c, attrs); } else { throw new RuntimeException("Unknown animation name: " + parser.getName()); } diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java new file mode 100644 index 000000000000..fd627e50ab0e --- /dev/null +++ b/core/java/android/view/animation/ExtendAnimation.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Insets; +import android.util.AttributeSet; + +/** + * An animation that controls the outset of an object. + * + * @hide + */ +public class ExtendAnimation extends Animation { + protected Insets mFromInsets = Insets.NONE; + protected Insets mToInsets = Insets.NONE; + + private int mFromLeftType = ABSOLUTE; + private int mFromTopType = ABSOLUTE; + private int mFromRightType = ABSOLUTE; + private int mFromBottomType = ABSOLUTE; + + private int mToLeftType = ABSOLUTE; + private int mToTopType = ABSOLUTE; + private int mToRightType = ABSOLUTE; + private int mToBottomType = ABSOLUTE; + + private float mFromLeftValue; + private float mFromTopValue; + private float mFromRightValue; + private float mFromBottomValue; + + private float mToLeftValue; + private float mToTopValue; + private float mToRightValue; + private float mToBottomValue; + + /** + * Constructor used when an ExtendAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public ExtendAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.ExtendAnimation); + + Description d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft)); + mFromLeftType = d.type; + mFromLeftValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendTop)); + mFromTopType = d.type; + mFromTopValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendRight)); + mFromRightType = d.type; + mFromRightValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom)); + mFromBottomType = d.type; + mFromBottomValue = d.value; + + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendLeft)); + mToLeftType = d.type; + mToLeftValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendTop)); + mToTopType = d.type; + mToTopValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendRight)); + mToRightType = d.type; + mToRightValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ExtendAnimation_toExtendBottom)); + mToBottomType = d.type; + mToBottomValue = d.value; + + a.recycle(); + } + + /** + * Constructor to use when building an ExtendAnimation from code + * + * @param fromInsets the insets to animate from + * @param toInsets the insets to animate to + */ + public ExtendAnimation(Insets fromInsets, Insets toInsets) { + if (fromInsets == null || toInsets == null) { + throw new RuntimeException("Expected non-null animation outsets"); + } + mFromLeftValue = -fromInsets.left; + mFromTopValue = -fromInsets.top; + mFromRightValue = -fromInsets.right; + mFromBottomValue = -fromInsets.bottom; + + mToLeftValue = -toInsets.left; + mToTopValue = -toInsets.top; + mToRightValue = -toInsets.right; + mToBottomValue = -toInsets.bottom; + } + + /** + * Constructor to use when building an ExtendAnimation from code + */ + public ExtendAnimation(int fromL, int fromT, int fromR, int fromB, + int toL, int toT, int toR, int toB) { + this(Insets.of(-fromL, -fromT, -fromR, -fromB), Insets.of(-toL, -toT, -toR, -toB)); + } + + @Override + protected void applyTransformation(float it, Transformation tr) { + int l = mFromInsets.left + (int) ((mToInsets.left - mFromInsets.left) * it); + int t = mFromInsets.top + (int) ((mToInsets.top - mFromInsets.top) * it); + int r = mFromInsets.right + (int) ((mToInsets.right - mFromInsets.right) * it); + int b = mFromInsets.bottom + (int) ((mToInsets.bottom - mFromInsets.bottom) * it); + tr.setInsets(l, t, r, b); + } + + @Override + public boolean willChangeTransformationMatrix() { + return false; + } + + /** @hide */ + @Override + public boolean hasExtension() { + return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0 + || mFromInsets.bottom < 0; + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + // We remove any negative extension (i.e. positive insets) and set those to 0 + mFromInsets = Insets.min(Insets.of( + -(int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth), + -(int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight), + -(int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth), + -(int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight) + ), Insets.NONE); + mToInsets = Insets.min(Insets.of( + -(int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth), + -(int) resolveSize(mToTopType, mToTopValue, height, parentHeight), + -(int) resolveSize(mToRightType, mToRightValue, width, parentWidth), + -(int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight) + ), Insets.NONE); + } +} diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java index b35a66e6eb26..bd623088017a 100644 --- a/core/java/android/view/animation/Transformation.java +++ b/core/java/android/view/animation/Transformation.java @@ -18,6 +18,7 @@ package android.view.animation; import android.annotation.FloatRange; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; @@ -53,6 +54,8 @@ public class Transformation { private boolean mHasClipRect; private Rect mClipRect = new Rect(); + private Insets mInsets = Insets.NONE; + /** * Creates a new transformation with alpha = 1 and the identity matrix. */ @@ -132,8 +135,9 @@ public class Transformation { setClipRect(bounds); } } + setInsets(Insets.add(getInsets(), t.getInsets())); } - + /** * Like {@link #compose(Transformation)} but does this.postConcat(t) of * the transformation matrix. @@ -160,7 +164,7 @@ public class Transformation { public Matrix getMatrix() { return mMatrix; } - + /** * Sets the degree of transparency * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent @@ -170,6 +174,13 @@ public class Transformation { } /** + * @return The degree of transparency + */ + public float getAlpha() { + return mAlpha; + } + + /** * Sets the current Transform's clip rect * @hide */ @@ -203,12 +214,29 @@ public class Transformation { } /** - * @return The degree of transparency + * Sets the current Transform's insets + * @hide */ - public float getAlpha() { - return mAlpha; + public void setInsets(Insets insets) { + mInsets = insets; } - + + /** + * Sets the current Transform's insets + * @hide + */ + public void setInsets(int left, int top, int right, int bottom) { + mInsets = Insets.of(left, top, right, bottom); + } + + /** + * Returns the current Transform's outset rect + * @hide + */ + public Insets getInsets() { + return mInsets; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(64); @@ -216,7 +244,7 @@ public class Transformation { toShortString(sb); return sb.toString(); } - + /** * Return a string representation of the transformation in a compact form. */ @@ -225,7 +253,7 @@ public class Transformation { toShortString(sb); return sb.toString(); } - + /** * @hide */ @@ -234,7 +262,7 @@ public class Transformation { sb.append(" matrix="); sb.append(mMatrix.toShortString()); sb.append('}'); } - + /** * Print short string, to optimize dumping. * @hide diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7c939aca09cf..0fe2ed51beb6 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12289,6 +12289,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener EXTRA_DATA_RENDERING_INFO_KEY, EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY )); + info.setTextSelectable(isTextSelectable()); } else { info.setAvailableExtraData(Arrays.asList( EXTRA_DATA_RENDERING_INFO_KEY diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index ece6f2f3571b..37c96e71a0a8 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -98,6 +98,10 @@ public class TransitionAnimation { private static final String DEFAULT_PACKAGE = "android"; + // TODO (b/215515255): remove once we full migrate to shell transitions + private static final boolean SHELL_TRANSITIONS_ENABLED = + SystemProperties.getBoolean("persist.debug.shell_transit", false); + private final Context mContext; private final String mTag; @@ -252,6 +256,9 @@ public class TransitionAnimation { resId = ent.array.getResourceId(animAttr, 0); } } + if (!SHELL_TRANSITIONS_ENABLED) { + resId = updateToLegacyIfNeeded(resId); + } resId = updateToTranslucentAnimIfNeeded(resId, transit); if (ResourceId.isValid(resId)) { return loadAnimationSafely(context, resId, mTag); @@ -259,6 +266,24 @@ public class TransitionAnimation { return null; } + /** + * Replace animations that are not compatible with the legacy transition system with ones that + * are compatible with it. + * TODO (b/215515255): remove once we full migrate to shell transitions + */ + private int updateToLegacyIfNeeded(int anim) { + if (anim == R.anim.activity_open_enter) { + return R.anim.activity_open_enter_legacy; + } else if (anim == R.anim.activity_open_exit) { + return R.anim.activity_open_exit_legacy; + } else if (anim == R.anim.activity_close_enter) { + return R.anim.activity_close_enter_legacy; + } else if (anim == R.anim.activity_close_exit) { + return R.anim.activity_close_exit_legacy; + } + return anim; + } + /** Load animation by attribute Id from a specific AnimationStyle resource. */ @Nullable public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr, diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp new file mode 100644 index 000000000000..362daa73ff14 --- /dev/null +++ b/core/proto/android/server/Android.bp @@ -0,0 +1,28 @@ +// +// Copyright (C) 2021 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "srcs_bluetooth_manager_service_proto", + srcs: [ + "bluetooth_manager_service.proto", + ], + visibility: ["//packages/modules/Bluetooth:__subpackages__"], +} + diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9bcd7d2d514c..85504ceae791 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4125,6 +4125,16 @@ <permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by a + android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService, + to ensure that only the system can bind to it. + @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE" + android:protectionLevel="signature" /> + + <!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). @@ -5378,7 +5388,7 @@ android:protectionLevel="signature|setup|role" /> <!-- Allows access to keyguard secure storage. Only allowed for system processes. - @hide @TestApi --> + @hide --> <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" android:protectionLevel="signature|setup" /> @@ -5824,6 +5834,13 @@ <permission android:name="android.permission.MANAGE_SMARTSPACE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to manage the wallpaper effects + generation service. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" + android:protectionLevel="signature" /> + + <!-- Allows an app to set the theme overlay in /vendor/overlay being used. @hide <p>Not for use by third-party applications.</p> --> @@ -5923,6 +5940,10 @@ <p>Not for use by third-party applications. --> <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" android:protectionLevel="signature" /> + <!-- @hide Permission that suppresses the notification when the clipboard is accessed. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" + android:protectionLevel="signature" /> <!-- @SystemApi Allows modifying accessibility state. @hide --> diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml index 9fa7c5498ea6..0fefb5113dfc 100644 --- a/core/res/res/anim/activity_close_enter.xml +++ b/core/res/res/anim/activity_close_enter.xml @@ -19,16 +19,37 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <scale - android:fromXScale="1.1" - android:toXScale="1" - android:fromYScale="1.1" - android:toYScale="1" - android:pivotX="50%" - android:pivotY="50%" + + <alpha + android:fromAlpha="1.0" + android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="0" + android:duration="450" /> + + <translate + android:fromXDelta="-10%" + android:toXDelta="0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="450" /> + + <extend + android:fromExtendLeft="0" + android:fromExtendTop="0" + android:fromExtendRight="10%" + android:fromExtendBottom="0" + android:toExtendLeft="0" + android:toExtendTop="0" + android:toExtendRight="10%" + android:toExtendBottom="0" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:startOffset="0" + android:duration="450" /> </set>
\ No newline at end of file diff --git a/core/res/res/anim/activity_close_enter_legacy.xml b/core/res/res/anim/activity_close_enter_legacy.xml new file mode 100644 index 000000000000..9fa7c5498ea6 --- /dev/null +++ b/core/res/res/anim/activity_close_enter_legacy.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + <scale + android:fromXScale="1.1" + android:toXScale="1" + android:fromYScale="1.1" + android:toYScale="1" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml index 1599ae8cb19f..f807c26dda20 100644 --- a/core/res/res/anim/activity_close_exit.xml +++ b/core/res/res/anim/activity_close_exit.xml @@ -18,27 +18,38 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" - android:zAdjustment="top"> + android:shareInterpolator="false"> + <alpha - android:fromAlpha="1" + android:fromAlpha="1.0" android:toAlpha="0.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/linear" - android:startOffset="33" - android:duration="50"/> - <scale - android:fromXScale="1" - android:toXScale="0.9" - android:fromYScale="1" - android:toYScale="0.9" - android:pivotX="50%" - android:pivotY="50%" + android:startOffset="35" + android:duration="83" /> + + <translate + android:fromXDelta="0" + android:toXDelta="10%" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:startOffset="0" + android:duration="450" /> + + <extend + android:fromExtendLeft="10%" + android:fromExtendTop="0" + android:fromExtendRight="0" + android:fromExtendBottom="0" + android:toExtendLeft="10%" + android:toExtendTop="0" + android:toExtendRight="0" + android:toExtendBottom="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="450" /> </set> diff --git a/core/res/res/anim/activity_close_exit_legacy.xml b/core/res/res/anim/activity_close_exit_legacy.xml new file mode 100644 index 000000000000..1599ae8cb19f --- /dev/null +++ b/core/res/res/anim/activity_close_exit_legacy.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="top"> + <alpha + android:fromAlpha="1" + android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="33" + android:duration="50"/> + <scale + android:fromXScale="1" + android:toXScale="0.9" + android:fromYScale="1" + android:toYScale="0.9" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> +</set> diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml index 38d3e8ed06ce..1674dab3040a 100644 --- a/core/res/res/anim/activity_open_enter.xml +++ b/core/res/res/anim/activity_open_enter.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> + <alpha android:fromAlpha="0" android:toAlpha="1.0" @@ -26,17 +27,27 @@ android:fillAfter="true" android:interpolator="@interpolator/linear" android:startOffset="50" - android:duration="50"/> - <scale - android:fromXScale="0.85" - android:toXScale="1" - android:fromYScale="0.85" - android:toYScale="1" - android:pivotX="50%" - android:pivotY="50%" + android:duration="83" /> + + <translate + android:fromXDelta="10%" + android:toXDelta="0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="450" /> + + <extend + android:fromExtendLeft="10%" + android:fromExtendTop="0" + android:fromExtendRight="0" + android:fromExtendBottom="0" + android:toExtendLeft="10%" + android:toExtendTop="0" + android:toExtendRight="0" + android:toExtendBottom="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="450" /> </set> diff --git a/core/res/res/anim/activity_open_enter_legacy.xml b/core/res/res/anim/activity_open_enter_legacy.xml new file mode 100644 index 000000000000..38d3e8ed06ce --- /dev/null +++ b/core/res/res/anim/activity_open_enter_legacy.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + <alpha + android:fromAlpha="0" + android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="50" + android:duration="50"/> + <scale + android:fromXScale="0.85" + android:toXScale="1" + android:fromYScale="0.85" + android:toYScale="1" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> +</set> diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml index 3865d2149f42..372f2c8e09f6 100644 --- a/core/res/res/anim/activity_open_exit.xml +++ b/core/res/res/anim/activity_open_exit.xml @@ -19,27 +19,36 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <!-- Fade out, over a black surface, which simulates a black scrim --> <alpha - android:fromAlpha="1" - android:toAlpha="0.4" + android:fromAlpha="1.0" + android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/linear" - android:startOffset="83" - android:duration="167"/> + android:interpolator="@interpolator/standard_accelerate" + android:startOffset="0" + android:duration="450" /> - <scale - android:fromXScale="1" - android:toXScale="1.05" - android:fromYScale="1" - android:toYScale="1.05" - android:pivotX="50%" - android:pivotY="50%" + <translate + android:fromXDelta="0" + android:toXDelta="-10%" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:startOffset="0" + android:duration="450" /> + + <extend + android:fromExtendLeft="0" + android:fromExtendTop="0" + android:fromExtendRight="10%" + android:fromExtendBottom="0" + android:toExtendLeft="0" + android:toExtendTop="0" + android:toExtendRight="10%" + android:toExtendBottom="0" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="450" /> </set>
\ No newline at end of file diff --git a/core/res/res/anim/activity_open_exit_legacy.xml b/core/res/res/anim/activity_open_exit_legacy.xml new file mode 100644 index 000000000000..3865d2149f42 --- /dev/null +++ b/core/res/res/anim/activity_open_exit_legacy.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false"> + + <!-- Fade out, over a black surface, which simulates a black scrim --> + <alpha + android:fromAlpha="1" + android:toAlpha="0.4" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/linear" + android:startOffset="83" + android:duration="167"/> + + <scale + android:fromXScale="1" + android:toXScale="1.05" + android:fromYScale="1" + android:toYScale="1.05" + android:pivotX="50%" + android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" + android:fillAfter="true" + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:duration="400"/> +</set>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7916ef43da50..afe0f1bf0001 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2885,7 +2885,7 @@ <code>public void sayHello(View v)</code> method of your context (typically, your Activity). {@deprecated View actually traverses the Context - hierarchy looking for the relevant method, which is fragile (an intermediate + hierarchy looking for the relevant method, which is fragile (an intermediate ContextWrapper adding a same-named method would change behavior) and restricts bytecode optimizers such as R8. Instead, use View.setOnClickListener.}--> <attr name="onClick" format="string" /> @@ -6975,6 +6975,34 @@ <attr name="toBottom" format="fraction" /> </declare-styleable> + <!-- Defines the ExtendAnimation used to extend windows during animations --> + <declare-styleable name="ExtendAnimation"> + <!-- Defines the amount a window should be extended outward from the left at + the start of the animation. --> + <attr name="fromExtendLeft" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the top at + the start of the animation. --> + <attr name="fromExtendTop" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the right at + the start of the animation. --> + <attr name="fromExtendRight" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the bottom at + the start of the animation. --> + <attr name="fromExtendBottom" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the left by + the end of the animation by transitioning from the fromExtendLeft amount. --> + <attr name="toExtendLeft" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the top by + the end of the animation by transitioning from the fromExtendTop amount. --> + <attr name="toExtendTop" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the right by + the end of the animation by transitioning from the fromExtendRight amount. --> + <attr name="toExtendRight" format="float|fraction" /> + <!-- Defines the amount a window should be extended outward from the bottom by + the end of the animation by transitioning from the fromExtendBottom amount. --> + <attr name="toExtendBottom" format="float|fraction" /> + </declare-styleable> + <declare-styleable name="LayoutAnimation"> <!-- Fraction of the animation duration used to delay the beginning of the animation of each child. --> @@ -7827,7 +7855,7 @@ <!-- Name of a method on the Context used to inflate the menu that will be called when the item is clicked. - {@deprecated Menu actually traverses the Context hierarchy looking for the + {@deprecated Menu actually traverses the Context hierarchy looking for the relevant method, which is fragile (an intermediate ContextWrapper adding a same-named method would change behavior) and restricts bytecode optimizers such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} --> @@ -8847,6 +8875,22 @@ <attr name="gameSessionService" format="string" /> </declare-styleable> + <!-- Use <code>game-mode-config</code> as the root tag of the XML resource that + describes a GameModeConfig. + Described here are the attributes that can be included in that tag. --> + <declare-styleable name="GameModeConfig"> + <!-- Set true to opt in BATTERY mode. --> + <attr name="supportsBatteryGameMode" format="boolean" /> + <!-- Set true to opt in PERFORMANCE mode. --> + <attr name="supportsPerformanceGameMode" format="boolean" /> + <!-- Set true to enable ANGLE. --> + <attr name="allowGameAngleDriver" format="boolean" /> + <!-- Set true to allow resolution downscaling intervention. --> + <attr name="allowGameDownscaling" format="boolean" /> + <!-- Set true to allow FPS override intervention. --> + <attr name="allowGameFpsOverride" format="boolean" /> + </declare-styleable> + <!-- Use <code>voice-enrollment-application</code> as the root tag of the XML resource that escribes the supported keyphrases (hotwords) by the enrollment application. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a06b2cb28037..902d5e0b9f32 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4173,6 +4173,15 @@ <string name="config_defaultMusicRecognitionService" translatable="false"></string> + <!-- The package name for the system's wallpaper effects generation service. + This service returns wallpaper effects results. + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, wallpaper effects + generation service will be disabled. + Example: "com.android.intelligence/.WallpaperEffectsGenerationService" +--> + <string name="config_defaultWallpaperEffectsGenerationService" translatable="false"></string> + <!-- The package name for the default retail demo app. This package must be trusted, as it has the permissions to query the usage stats on the device. @@ -5267,6 +5276,12 @@ <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool> <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool> + <bool name="config_cecSetMenuLanguage_userConfigurable">true</bool> + <bool name="config_cecSetMenuLanguageEnabled_allowed">true</bool> + <bool name="config_cecSetMenuLanguageEnabled_default">true</bool> + <bool name="config_cecSetMenuLanguageDisabled_allowed">true</bool> + <bool name="config_cecSetMenuLanguageDisabled_default">false</bool> + <bool name="config_cecRcProfileTv_userConfigurable">true</bool> <bool name="config_cecRcProfileTvNone_allowed">true</bool> <bool name="config_cecRcProfileTvNone_default">true</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2e96c65ef17f..d57f5ba8179b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3255,11 +3255,24 @@ <public name="showClockAndComplications" /> <!-- @hide @SystemApi --> <public name="gameSessionService" /> + <public name="supportsBatteryGameMode" /> + <public name="supportsPerformanceGameMode" /> + <public name="allowGameAngleDriver" /> + <public name="allowGameDownscaling" /> + <public name="allowGameFpsOverride" /> <public name="localeConfig" /> <public name="showBackground" /> <public name="inheritKeyStoreKeys" /> <public name="preferKeepClear" /> <public name="autoHandwritingEnabled" /> + <public name="fromExtendLeft" /> + <public name="fromExtendTop" /> + <public name="fromExtendRight" /> + <public name="fromExtendBottom" /> + <public name="toExtendLeft" /> + <public name="toExtendTop" /> + <public name="toExtendRight" /> + <public name="toExtendBottom" /> </staging-public-group> <staging-public-group type="id" first-id="0x01de0000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 610c6a69822c..2e4b783a6dca 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5857,6 +5857,8 @@ <string name="accessibility_system_action_lock_screen_label">Lock Screen</string> <!-- Label for taking screenshot action [CHAR LIMIT=NONE] --> <string name="accessibility_system_action_screenshot_label">Screenshot</string> + <!-- Label for headset hook action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_headset_hook_label">Headset Hook</string> <!-- Label for triggering on-screen accessibility shortcut action [CHAR LIMIT=NONE] --> <string name="accessibility_system_action_on_screen_a11y_shortcut_label">On-screen Accessibility Shortcut</string> <!-- Label for showing on-screen accessibility shortcut chooser action [CHAR LIMIT=NONE] --> @@ -5865,6 +5867,16 @@ <string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string> <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] --> <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string> + <!-- Label for Dpad up action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_dpad_up_label">Dpad Up</string> + <!-- Label for Dpad down action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_dpad_down_label">Dpad Down</string> + <!-- Label for Dpad left action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_dpad_left_label">Dpad Left</string> + <!-- Label for Dpad right action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_dpad_right_label">Dpad Right</string> + <!-- Label for Dpad center action [CHAR LIMIT=NONE] --> + <string name="accessibility_system_action_dpad_center_label">Dpad Center</string> <!-- Accessibility description of caption view --> <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4c1cc4dc8f70..30a196395b35 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1697,7 +1697,13 @@ <java-symbol type="anim" name="activity_translucent_open_enter" /> <java-symbol type="anim" name="activity_translucent_close_exit" /> <java-symbol type="anim" name="activity_open_enter" /> + <java-symbol type="anim" name="activity_open_exit" /> + <java-symbol type="anim" name="activity_close_enter" /> <java-symbol type="anim" name="activity_close_exit" /> + <java-symbol type="anim" name="activity_open_enter_legacy" /> + <java-symbol type="anim" name="activity_open_exit_legacy" /> + <java-symbol type="anim" name="activity_close_enter_legacy" /> + <java-symbol type="anim" name="activity_close_exit_legacy" /> <java-symbol type="anim" name="task_fragment_close_enter" /> <java-symbol type="anim" name="task_fragment_close_exit" /> <java-symbol type="anim" name="task_fragment_open_enter" /> @@ -3673,6 +3679,7 @@ <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultSearchUiService" /> <java-symbol type="string" name="config_defaultSmartspaceService" /> + <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" /> <java-symbol type="string" name="config_defaultMusicRecognitionService" /> <java-symbol type="string" name="config_defaultAttentionService" /> <java-symbol type="string" name="config_defaultRotationResolverService" /> @@ -4129,10 +4136,16 @@ <java-symbol type="string" name="accessibility_system_action_quick_settings_label" /> <java-symbol type="string" name="accessibility_system_action_recents_label" /> <java-symbol type="string" name="accessibility_system_action_screenshot_label" /> + <java-symbol type="string" name="accessibility_system_action_headset_hook_label" /> <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" /> <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" /> <java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" /> <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" /> + <java-symbol type="string" name="accessibility_system_action_dpad_up_label" /> + <java-symbol type="string" name="accessibility_system_action_dpad_down_label" /> + <java-symbol type="string" name="accessibility_system_action_dpad_left_label" /> + <java-symbol type="string" name="accessibility_system_action_dpad_right_label" /> + <java-symbol type="string" name="accessibility_system_action_dpad_center_label" /> <java-symbol type="string" name="accessibility_freeform_caption" /> @@ -4453,6 +4466,12 @@ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" /> <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" /> + <java-symbol type="bool" name="config_cecSetMenuLanguage_userConfigurable" /> + <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_allowed" /> + <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_default" /> + <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_allowed" /> + <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_default" /> + <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" /> <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" /> <java-symbol type="bool" name="config_cecRcProfileTvNone_default" /> diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index d3e8bb0ec317..5338d046596a 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -37,9 +37,9 @@ import org.junit.Test; @SmallTest public class PropertyInvalidatedCacheTests { - // This property is never set. The test process does not have permission to set any - // properties. - static final String CACHE_PROPERTY = "cache_key.cache_test_a"; + // Configuration for creating caches + private static final int MODULE = PropertyInvalidatedCache.MODULE_TEST; + private static final String API = "testApi"; // This class is a proxy for binder calls. It contains a counter that increments // every time the class is queried. @@ -64,6 +64,25 @@ public class PropertyInvalidatedCacheTests { } } + // The functions for querying the server. + private static class ServerQuery + extends PropertyInvalidatedCache.QueryHandler<Integer, Boolean> { + private final ServerProxy mServer; + + ServerQuery(ServerProxy server) { + mServer = server; + } + + @Override + public Boolean apply(Integer x) { + return mServer.query(x); + } + @Override + public boolean shouldBypassCache(Integer x) { + return x % 13 == 0; + } + } + // Clear the test mode after every test, in case this process is used for other // tests. This also resets the test property map. @After @@ -82,19 +101,11 @@ public class PropertyInvalidatedCacheTests { // Create a cache that uses simple arithmetic to computer its values. PropertyInvalidatedCache<Integer, Boolean> testCache = - new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - @Override - public boolean bypass(Integer x) { - return x % 13 == 0; - } - }; + new PropertyInvalidatedCache<>(4, MODULE, API, "cache1", + new ServerQuery(tester)); PropertyInvalidatedCache.setTestMode(true); - PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY); + testCache.testPropertyName(); tester.verify(0); assertEquals(tester.value(3), testCache.query(3)); @@ -136,26 +147,14 @@ public class PropertyInvalidatedCacheTests { // Three caches, all using the same system property but one uses a different name. PropertyInvalidatedCache<Integer, Boolean> cache1 = - new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - }; + new PropertyInvalidatedCache<>(4, MODULE, API, "cache1", + new ServerQuery(tester)); PropertyInvalidatedCache<Integer, Boolean> cache2 = - new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - }; + new PropertyInvalidatedCache<>(4, MODULE, API, "cache1", + new ServerQuery(tester)); PropertyInvalidatedCache<Integer, Boolean> cache3 = - new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - }; + new PropertyInvalidatedCache<>(4, MODULE, API, "cache3", + new ServerQuery(tester)); // Caches are enabled upon creation. assertEquals(false, cache1.getDisabledState()); @@ -176,61 +175,70 @@ public class PropertyInvalidatedCacheTests { assertEquals(false, cache3.getDisabledState()); // Create a new cache1. Verify that the new instance is disabled. - cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - }; + cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1", + new ServerQuery(tester)); assertEquals(true, cache1.getDisabledState()); // Remove the record of caches being locally disabled. This is a clean-up step. - cache1.clearDisableLocal(); + cache1.forgetDisableLocal(); assertEquals(true, cache1.getDisabledState()); assertEquals(true, cache2.getDisabledState()); assertEquals(false, cache3.getDisabledState()); // Create a new cache1. Verify that the new instance is not disabled. - cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { - @Override - public Boolean recompute(Integer x) { - return tester.query(x); - } - }; + cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1", + new ServerQuery(tester)); assertEquals(false, cache1.getDisabledState()); } - private static final String UNSET_KEY = "Aiw7woh6ie4toh7W"; + private static class TestQuery + extends PropertyInvalidatedCache.QueryHandler<Integer, String> { + + private int mRecomputeCount = 0; + + @Override + public String apply(Integer qv) { + mRecomputeCount += 1; + return "foo" + qv.toString(); + } + + int getRecomputeCount() { + return mRecomputeCount; + } + } private static class TestCache extends PropertyInvalidatedCache<Integer, String> { + private final TestQuery mQuery; + TestCache() { - this(CACHE_PROPERTY); + this(MODULE, API); } - TestCache(String key) { - super(4, key); + TestCache(int module, String api) { + this(module, api, new TestQuery()); setTestMode(true); - testPropertyName(key); + testPropertyName(); } - @Override - public String recompute(Integer qv) { - mRecomputeCount += 1; - return "foo" + qv.toString(); + TestCache(int module, String api, TestQuery query) { + super(4, module, api, api, query); + mQuery = query; + setTestMode(true); + testPropertyName(); } - int getRecomputeCount() { - return mRecomputeCount; + public int getRecomputeCount() { + return mQuery.getRecomputeCount(); } - private int mRecomputeCount = 0; + } @Test public void testCacheRecompute() { TestCache cache = new TestCache(); cache.invalidateCache(); - assertEquals(cache.isDisabledLocal(), false); + assertEquals(cache.isDisabled(), false); assertEquals("foo5", cache.query(5)); assertEquals(1, cache.getRecomputeCount()); assertEquals("foo5", cache.query(5)); @@ -241,6 +249,11 @@ public class PropertyInvalidatedCacheTests { assertEquals("foo5", cache.query(5)); assertEquals("foo5", cache.query(5)); assertEquals(3, cache.getRecomputeCount()); + // Invalidate the cache with a direct call to the property. + PropertyInvalidatedCache.invalidateCache(MODULE, API); + assertEquals("foo5", cache.query(5)); + assertEquals("foo5", cache.query(5)); + assertEquals(4, cache.getRecomputeCount()); } @Test @@ -257,7 +270,8 @@ public class PropertyInvalidatedCacheTests { @Test public void testCachePropertyUnset() { - TestCache cache = new TestCache(UNSET_KEY); + final String UNSET_API = "otherApi"; + TestCache cache = new TestCache(MODULE, UNSET_API); assertEquals("foo5", cache.query(5)); assertEquals("foo5", cache.query(5)); assertEquals(2, cache.getRecomputeCount()); @@ -327,17 +341,40 @@ public class PropertyInvalidatedCacheTests { @Test public void testLocalProcessDisable() { TestCache cache = new TestCache(); - assertEquals(cache.isDisabledLocal(), false); + assertEquals(cache.isDisabled(), false); cache.invalidateCache(); assertEquals("foo5", cache.query(5)); assertEquals(1, cache.getRecomputeCount()); assertEquals("foo5", cache.query(5)); assertEquals(1, cache.getRecomputeCount()); - assertEquals(cache.isDisabledLocal(), false); + assertEquals(cache.isDisabled(), false); cache.disableLocal(); - assertEquals(cache.isDisabledLocal(), true); + assertEquals(cache.isDisabled(), true); assertEquals("foo5", cache.query(5)); assertEquals("foo5", cache.query(5)); assertEquals(3, cache.getRecomputeCount()); } + + @Test + public void testPropertyNames() { + String n1; + n1 = PropertyInvalidatedCache.createPropertyName( + PropertyInvalidatedCache.MODULE_SYSTEM, "getPackageInfo"); + assertEquals(n1, "cache_key.system_server.get_package_info"); + n1 = PropertyInvalidatedCache.createPropertyName( + PropertyInvalidatedCache.MODULE_SYSTEM, "get_package_info"); + assertEquals(n1, "cache_key.system_server.get_package_info"); + try { + n1 = PropertyInvalidatedCache.createPropertyName( + PropertyInvalidatedCache.MODULE_SYSTEM - 1, "get package_info"); + // n1 is an invalid api name. + assertEquals(false, true); + } catch (IllegalArgumentException e) { + // An exception is expected here. + } + + n1 = PropertyInvalidatedCache.createPropertyName( + PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState"); + assertEquals(n1, "cache_key.bluetooth.get_state"); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 201883689546..99670d98bb84 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -58,7 +58,7 @@ public class AccessibilityNodeInfoTest { // The number of flags held in boolean properties. Their values should also be double-checked // in the methods above. - private static final int NUM_BOOLEAN_PROPERTIES = 23; + private static final int NUM_BOOLEAN_PROPERTIES = 24; @Test public void testStandardActions_serializationFlagIsValid() { diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index f04a9f735881..e16a2f8e8560 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -288,9 +288,8 @@ public class HdmiAudioSystemClientTest { } @Override - public void addVendorCommandListener(final IHdmiVendorCommandListener listener, - final int deviceType) { - } + public void addVendorCommandListener( + final IHdmiVendorCommandListener listener, final int vendorId) {} @Override public void sendVendorCommand(final int deviceType, final int targetAddress, diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index ddcab6eb76c8..5dcc5998850b 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -61,5 +61,6 @@ <permission name="android.permission.READ_DREAM_SUPPRESSION"/> <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> <permission name="android.permission.READ_SAFETY_CENTER_STATUS" /> + <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" /> </privapp-permissions> </permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b3dcc34d6e93..d0026016ecf4 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -581,6 +581,7 @@ applications that come with the platform <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/> <permission name="android.permission.READ_SAFETY_CENTER_STATUS" /> + <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" /> </privapp-permissions> <privapp-permissions package="com.android.bips"> diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index cdf746fc9900..f440b693a5b3 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -454,7 +454,8 @@ public abstract class IdentityCredential { * @param challenge is a non-empty byte array whose contents should be unique, fresh and * provided by the issuing authority. The value provided is embedded in the * generated CBOR and enables the issuing authority to verify that the - * returned proof is fresh. + * returned proof is fresh. Implementations are required to support + * challenges at least 32 bytes of length. * @return the COSE_Sign1 data structure above */ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) { @@ -485,7 +486,8 @@ public abstract class IdentityCredential { * @param challenge is a non-empty byte array whose contents should be unique, fresh and * provided by the issuing authority. The value provided is embedded in the * generated CBOR and enables the issuing authority to verify that the - * returned proof is fresh. + * returned proof is fresh. Implementations are required to support + * challenges at least 32 bytes of length. * @return the COSE_Sign1 data structure above */ public @NonNull byte[] delete(@NonNull byte[] challenge) { diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java index 305d0ead0652..6d569648f2c6 100644 --- a/identity/java/android/security/identity/WritableIdentityCredential.java +++ b/identity/java/android/security/identity/WritableIdentityCredential.java @@ -59,7 +59,8 @@ public abstract class WritableIdentityCredential { * @param challenge is a non-empty byte array whose contents should be unique, fresh and * provided by the issuing authority. The value provided is embedded in the * attestation extension and enables the issuing authority to verify that the - * attestation certificate is fresh. + * attestation certificate is fresh. Implementations are required to support + * challenges at least 32 bytes of length. * @return the X.509 certificate for this credential's CredentialKey. */ public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 17005ea7d500..6b0d7f5fa461 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -867,6 +867,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void fadeExistingPip(boolean show) { + if (mLeash == null || !mLeash.isValid()) { + Log.w(TAG, "Invalid leash on fadeExistingPip: " + mLeash); + return; + } final float alphaStart = show ? 0 : 1; final float alphaEnd = show ? 1 : 0; mPipAnimationController diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 27b6dc5b304f..ddf01a8c5ee9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -67,7 +67,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -78,6 +82,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArrayMap; import android.view.Choreographer; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; @@ -103,6 +108,8 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @@ -331,6 +338,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); }; + final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks = + new ArrayList<>(); + @ColorInt int backgroundColorForTransition = 0; final int wallpaperTransit = getWallpaperTransitType(info); for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -402,13 +412,15 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - float cornerRadius = 0; + final float cornerRadius; if (a.hasRoundedCorners() && isTask) { // hasRoundedCorners is currently only enabled for tasks final Context displayContext = mDisplayController.getDisplayContext(change.getTaskInfo().displayId); cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(displayContext); + } else { + cornerRadius = 0; } if (a.getShowBackground()) { @@ -424,12 +436,37 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } + boolean delayedEdgeExtension = false; + if (!isTask && a.hasExtension()) { + if (!Transitions.isOpeningType(change.getMode())) { + // Can screenshot now (before startTransaction is applied) + edgeExtendWindow(change, a, startTransaction, finishTransaction); + } else { + // Need to screenshot after startTransaction is applied otherwise activity + // may not be visible or ready yet. + postStartTransactionCallbacks + .add(t -> edgeExtendWindow(change, a, t, finishTransaction)); + delayedEdgeExtension = true; + } + } + final Rect clipRect = Transitions.isClosingType(change.getMode()) ? mRotator.getEndBoundsInStartRotation(change) : change.getEndAbsBounds(); - startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish, - mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */, - cornerRadius, clipRect); + + if (delayedEdgeExtension) { + // If the edge extension needs to happen after the startTransition has been + // applied, then we want to only start the animation after the edge extension + // postStartTransaction callback has been run + postStartTransactionCallbacks.add(t -> + startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish, + mTransactionPool, mMainExecutor, mAnimExecutor, + null /* position */, cornerRadius, clipRect)); + } else { + startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish, + mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */, + cornerRadius, clipRect); + } if (info.getAnimationOptions() != null) { attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(), @@ -443,7 +480,20 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { startTransaction, finishTransaction); } - startTransaction.apply(); + // postStartTransactionCallbacks require that the start transaction is already + // applied to run otherwise they may result in flickers and UI inconsistencies. + boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0; + startTransaction.apply(waitForStartTransactionApply); + + // Run tasks that require startTransaction to already be applied + for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback : + postStartTransactionCallbacks) { + final SurfaceControl.Transaction t = mTransactionPool.acquire(); + postStartTransactionCallback.accept(t); + t.apply(); + mTransactionPool.release(t); + } + mRotator.cleanUp(finishTransaction); TransitionMetrics.getInstance().reportAnimationStart(transition); // run finish now in-case there are no animations @@ -451,6 +501,117 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return true; } + private void edgeExtendWindow(TransitionInfo.Change change, + Animation a, SurfaceControl.Transaction startTransaction, + SurfaceControl.Transaction finishTransaction) { + final Transformation transformationAtStart = new Transformation(); + a.getTransformationAt(0, transformationAtStart); + final Transformation transformationAtEnd = new Transformation(); + a.getTransformationAt(1, transformationAtEnd); + + // We want to create an extension surface that is the maximal size and the animation will + // take care of cropping any part that overflows. + final Insets maxExtensionInsets = Insets.min( + transformationAtStart.getInsets(), transformationAtEnd.getInsets()); + + final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(), + change.getEndAbsBounds().height()); + final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(), + change.getEndAbsBounds().width()); + if (maxExtensionInsets.left < 0) { + final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + -maxExtensionInsets.left, targetSurfaceHeight); + final int xPos = maxExtensionInsets.left; + final int yPos = 0; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Left Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.top < 0) { + final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1); + final Rect extensionRect = new Rect(0, 0, + targetSurfaceWidth, -maxExtensionInsets.top); + final int xPos = 0; + final int yPos = maxExtensionInsets.top; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Top Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.right < 0) { + final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0, + targetSurfaceWidth, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + -maxExtensionInsets.right, targetSurfaceHeight); + final int xPos = targetSurfaceWidth; + final int yPos = 0; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Right Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.bottom < 0) { + final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1, + targetSurfaceWidth, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + targetSurfaceWidth, -maxExtensionInsets.bottom); + final int xPos = maxExtensionInsets.left; + final int yPos = targetSurfaceHeight; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Bottom Edge Extension", startTransaction, finishTransaction); + } + } + + private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds, + Rect extensionRect, int xPos, int yPos, String layerName, + SurfaceControl.Transaction startTransaction, + SurfaceControl.Transaction finishTransaction) { + final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() + .setName(layerName) + .setParent(surfaceToExtend) + .setHidden(true) + .setCallsite("DefaultTransitionHandler#startAnimation") + .setOpaque(true) + .setBufferSize(extensionRect.width(), extensionRect.height()) + .build(); + + SurfaceControl.LayerCaptureArgs captureArgs = + new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend) + .setSourceCrop(edgeBounds) + .setFrameScale(1) + .setPixelFormat(PixelFormat.RGBA_8888) + .setChildrenOnly(true) + .setAllowProtected(true) + .build(); + final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer = + SurfaceControl.captureLayers(captureArgs); + + if (edgeBuffer == null) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "Failed to capture edge of window."); + return null; + } + + android.graphics.BitmapShader shader = + new android.graphics.BitmapShader(edgeBuffer.asBitmap(), + android.graphics.Shader.TileMode.CLAMP, + android.graphics.Shader.TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setShader(shader); + + final Surface surface = new Surface(edgeExtensionLayer); + Canvas c = surface.lockHardwareCanvas(); + c.drawRect(extensionRect, paint); + surface.unlockCanvasAndPost(c); + surface.release(); + + startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); + startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); + startTransaction.setVisibility(edgeExtensionLayer, true); + finishTransaction.remove(edgeExtensionLayer); + + return edgeExtensionLayer; + } + private void addBackgroundToTransition( @NonNull SurfaceControl rootLeash, @ColorInt int color, @@ -778,9 +939,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } t.setMatrix(leash, transformation.getMatrix(), matrix); t.setAlpha(leash, transformation.getAlpha()); + + Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE); + if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) { + // Clip out any overflowing edge extension + clipRect.inset(extensionInsets); + t.setCrop(leash, clipRect); + } + if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) { // We can only apply rounded corner if a crop is set - t.setWindowCrop(leash, clipRect); + t.setCrop(leash, clipRect); t.setCornerRadius(leash, cornerRadius); } diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java index 86dc6e0f09c5..88b9777e911d 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -27,6 +27,7 @@ import android.os.Parcelable; */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class BtProfileConnectionInfo implements Parcelable { + private final int mProfile; private final boolean mSupprNoisy; private final int mVolume; diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java index 7f4e403f2259..798e9c3b52b5 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java +++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java @@ -20,22 +20,34 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** @hide */ public final class InternalNetworkManagementException extends RuntimeException implements Parcelable { /* @hide */ - public InternalNetworkManagementException(@NonNull final Throwable t) { - super(t); + public InternalNetworkManagementException(@NonNull final String errorMessage) { + super(errorMessage); + } + + @Override + public int hashCode() { + return Objects.hash(getMessage()); } - private InternalNetworkManagementException(@NonNull final Parcel source) { - super(source.readString()); + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + final InternalNetworkManagementException that = (InternalNetworkManagementException) obj; + + return Objects.equals(getMessage(), that.getMessage()); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(getCause().getMessage()); + dest.writeString(getMessage()); } @Override @@ -53,7 +65,7 @@ public final class InternalNetworkManagementException @Override public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) { - return new InternalNetworkManagementException(source); + return new InternalNetworkManagementException(source.readString()); } }; } diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 93e3deef7aa5..29a1831d28cb 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -647,6 +647,11 @@ <item>disabled</item> </array> + <!-- Images offered as options in the avatar picker. If populated, the avatar_image_descriptions + array must also be populated with a content description for each image. --> <array name="avatar_images"/> + <!-- Content descriptions for each of the images in the avatar_images array. --> + <string-array name="avatar_image_descriptions"/> + </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index af6a658f362a..0fe869fc44ef 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1562,4 +1562,8 @@ <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] --> <string name="avatar_picker_title">Choose a profile picture</string> + + <!-- Content description for a default user icon. [CHAR LIMIT=NONE] --> + <string name="default_user_icon_description">Default user icon</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java index 50015e653399..93be66ad4882 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java @@ -45,6 +45,7 @@ import com.google.android.setupdesign.GlifLayout; import com.google.android.setupdesign.util.ThemeHelper; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -180,6 +181,7 @@ public class AvatarPickerActivity extends Activity { private final int mPreselectedImageStartPosition; private final List<Drawable> mImageDrawables; + private final List<String> mImageDescriptions; private final TypedArray mPreselectedImages; private final int[] mUserIconColors; private int mSelectedPosition = NONE; @@ -196,6 +198,7 @@ public class AvatarPickerActivity extends Activity { mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images); mUserIconColors = UserIcons.getUserIconColors(getResources()); mImageDrawables = buildDrawableList(); + mImageDescriptions = buildDescriptionsList(); } @NonNull @@ -210,15 +213,24 @@ public class AvatarPickerActivity extends Activity { public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) { if (position == mTakePhotoPosition) { viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled)); + viewHolder.setContentDescription(getString(R.string.user_image_take_photo)); viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto()); } else if (position == mChoosePhotoPosition) { viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled)); + viewHolder.setContentDescription(getString(R.string.user_image_choose_photo)); viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto()); } else if (position >= mPreselectedImageStartPosition) { + int index = indexFromPosition(position); viewHolder.setSelected(position == mSelectedPosition); - viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position))); + viewHolder.setDrawable(mImageDrawables.get(index)); + if (mImageDescriptions != null) { + viewHolder.setContentDescription(mImageDescriptions.get(index)); + } else { + viewHolder.setContentDescription( + getString(R.string.default_user_icon_description)); + } viewHolder.setClickListener(view -> { if (mSelectedPosition == position) { deselect(position); @@ -256,6 +268,15 @@ public class AvatarPickerActivity extends Activity { return result; } + private List<String> buildDescriptionsList() { + if (mPreselectedImages.length() > 0) { + return Arrays.asList( + getResources().getStringArray(R.array.avatar_image_descriptions)); + } + + return null; + } + private Drawable circularDrawableFrom(BitmapDrawable drawable) { Bitmap bitmap = drawable.getBitmap(); @@ -323,6 +344,10 @@ public class AvatarPickerActivity extends Activity { mImageView.setImageDrawable(drawable); } + public void setContentDescription(String desc) { + mImageView.setContentDescription(desc); + } + public void setClickListener(View.OnClickListener listener) { mImageView.setOnClickListener(listener); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 27fc6ba47ec9..ca90fbedd4e0 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -192,6 +192,9 @@ <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" /> + <!-- Permission required for processes that don't own the focused window to switch + touch mode state --> + <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" /> <!-- Permission required to test onPermissionsChangedListener --> <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" /> <uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 55bf6e531fa2..f83431b58c62 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -213,6 +213,9 @@ <!-- DevicePolicyManager get user restrictions --> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> + <!-- DevicePolicyManager get admin policy --> + <uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" /> + <!-- TV picture-in-picture --> <uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" /> @@ -305,6 +308,7 @@ <!-- To change system language (HDMI CEC) --> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <uses-permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" /> <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> @@ -875,6 +879,12 @@ android:name=".media.taptotransfer.sender.MediaTttSenderService" /> + <!-- Service for external clients to notify us of nearby media devices --> + <!-- TODO(b/216313420): Export and guard with a permission. --> + <service + android:name=".media.nearby.NearbyMediaDevicesService" + /> + <receiver android:name=".tuner.TunerService$ClearReceiver" android:exported="false"> diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index dee4ff5a0bf5..9722b1fe2d2e 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -1,7 +1,7 @@ { // Looking for unit test presubmit configuration? // This currently lives in ATP config apct/system_ui/unit_test - "presubmit": [ + "presubmit-large": [ { "name": "PlatformScenarioTests", "options": [ @@ -24,7 +24,9 @@ "exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly" } ] - }, + } + ], + "presubmit": [ { "name": "SystemUIGoogleTests", "options": [ diff --git a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml index c547c52a4077..ec9465b20d61 100644 --- a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml +++ b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml @@ -20,6 +20,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z" + android:pathData="@*android:string/config_work_badge_path_24" android:fillColor="?android:attr/colorAccent"/> </vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml index 1e0ce0026d8e..0ff1db2ef694 100644 --- a/packages/SystemUI/res/layout/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml @@ -18,64 +18,72 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:gravity="center_horizontal" - android:elevation="@dimen/biometric_dialog_elevation"> + android:elevation="@dimen/biometric_dialog_elevation" + android:orientation="vertical"> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> - <ImageView - android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> + <LinearLayout + android:id="@+id/auth_credential_header" + style="@style/AuthCredentialHeaderStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true"> - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Title"/> + <ImageView + android:id="@+id/icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:contentDescription="@null" /> - <TextView - android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Subtitle"/> + <TextView + android:id="@+id/title" + style="@style/TextAppearance.AuthNonBioCredential.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> - <TextView - android:id="@+id/description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Description"/> + <TextView + android:id="@+id/subtitle" + style="@style/TextAppearance.AuthNonBioCredential.Subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> + <TextView + android:id="@+id/description" + style="@style/TextAppearance.AuthNonBioCredential.Description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> - <ImeAwareEditText - android:id="@+id/lockPassword" - android:layout_width="208dp" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:minHeight="48dp" - android:gravity="center" - android:inputType="textPassword" - android:maxLength="500" - android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" - style="@style/TextAppearance.AuthCredential.PasswordEntry"/> + </LinearLayout> - <TextView - android:id="@+id/error" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Error"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" + android:layout_alignParentBottom="true"> + + <ImeAwareEditText + android:id="@+id/lockPassword" + style="@style/TextAppearance.AuthCredential.PasswordEntry" + android:layout_width="208dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" + android:inputType="textPassword" + android:minHeight="48dp" /> + + <TextView + android:id="@+id/error" + style="@style/TextAppearance.AuthNonBioCredential.Error" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + </LinearLayout> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="5"/> + </RelativeLayout> </com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml index 4939ea2c99ee..dada9813c320 100644 --- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml @@ -22,76 +22,81 @@ android:gravity="center_horizontal" android:elevation="@dimen/biometric_dialog_elevation"> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> - - <ImageView - android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView - android:id="@+id/title" + <RelativeLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Title"/> + android:layout_height="match_parent" + android:orientation="vertical"> - <TextView - android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Subtitle"/> + <LinearLayout + android:id="@+id/auth_credential_header" + style="@style/AuthCredentialHeaderStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - <TextView - android:id="@+id/description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Description"/> + <ImageView + android:id="@+id/icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:contentDescription="@null" /> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> + <TextView + android:id="@+id/title" + style="@style/TextAppearance.AuthNonBioCredential.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:gravity="center" - android:paddingLeft="0dp" - android:paddingRight="0dp" - android:paddingTop="0dp" - android:paddingBottom="16dp" - android:clipToPadding="false"> - - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="0dp" - android:layout_weight="1" - style="@style/LockPatternContainerStyle"> - - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPattern" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - style="@style/LockPatternStyleBiometricPrompt"/> + <TextView + android:id="@+id/subtitle" + style="@style/TextAppearance.AuthNonBioCredential.Subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <TextView + android:id="@+id/description" + style="@style/TextAppearance.AuthNonBioCredential.Description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/auth_credential_header" + android:gravity="center" + android:orientation="vertical" + android:paddingBottom="16dp" + android:paddingTop="60dp"> + + <FrameLayout + style="@style/LockPatternContainerStyle" + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1"> + + <com.android.internal.widget.LockPatternView + android:id="@+id/lockPattern" + style="@style/LockPatternStyle" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> - </FrameLayout> + </FrameLayout> - <TextView - android:id="@+id/error" + </LinearLayout> + + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/TextAppearance.AuthCredential.Error"/> + android:layout_alignParentBottom="true"> + + <TextView + android:id="@+id/error" + style="@style/TextAppearance.AuthNonBioCredential.Error" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> - </LinearLayout> + </LinearLayout> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> + </RelativeLayout> </com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml new file mode 100644 index 000000000000..b6f516fd2042 --- /dev/null +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. +--> +<TextClock + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/date_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/dream_overlay_complication_clock_date_padding_left" + android:paddingBottom="@dimen/dream_overlay_complication_clock_date_padding_bottom" + android:gravity="center_horizontal" + android:textColor="@android:color/white" + android:shadowColor="@color/keyguard_shadow_color" + android:shadowRadius="?attr/shadowRadius" + android:format12Hour="EEE, MMM d" + android:format24Hour="EEE, MMM d" + android:textSize="@dimen/dream_overlay_complication_clock_date_text_size"/> diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml new file mode 100644 index 000000000000..a41d34f8ab41 --- /dev/null +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. +--> +<TextClock + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/time_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/dream_overlay_complication_clock_time_padding_left" + android:fontFamily="sans-serif-thin" + android:textColor="@android:color/white" + android:format12Hour="h:mm" + android:format24Hour="kk:mm" + android:shadowColor="@color/keyguard_shadow_color" + android:shadowRadius="?attr/shadowRadius" + android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/> diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml new file mode 100644 index 000000000000..08f0d6781b04 --- /dev/null +++ b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. +--> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/weather_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/dream_overlay_complication_weather_padding_left" + android:paddingBottom="@dimen/dream_overlay_complication_weather_padding_bottom" + android:textColor="@android:color/white" + android:shadowColor="@color/keyguard_shadow_color" + android:shadowRadius="?attr/shadowRadius" + android:textSize="@dimen/dream_overlay_complication_weather_text_size"/> diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml index 2d565a1a04fc..f4eb32f7cf22 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml @@ -20,34 +20,6 @@ android:id="@+id/dream_overlay_complications_layer" android:layout_width="match_parent" android:layout_height="match_parent"> - <TextClock - android:id="@+id/time_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:fontFamily="sans-serif-thin" - android:format12Hour="h:mm" - android:format24Hour="kk:mm" - android:shadowColor="#B2000000" - android:shadowRadius="2.0" - android:singleLine="true" - android:textSize="72sp" - android:textColor="@android:color/white" - app:layout_constraintBottom_toTopOf="@+id/date_view" - app:layout_constraintStart_toStartOf="parent" /> - <TextClock - android:id="@+id/date_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:shadowColor="#B2000000" - android:shadowRadius="2.0" - android:format12Hour="EEE, MMM d" - android:format24Hour="EEE, MMM d" - android:singleLine="true" - android:textSize="18sp" - android:textColor="@android:color/white" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="@+id/time_view" - app:layout_constraintStart_toStartOf="@+id/time_view" /> <androidx.constraintlayout.widget.Guideline android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index 4929f502fef0..3c2183d0ca61 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -25,7 +25,7 @@ android:id="@+id/dream_overlay_content" android:layout_width="match_parent" android:layout_height="0dp" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintTop_toBottomOf="@id/dream_overlay_status_bar" app:layout_constraintBottom_toBottomOf="parent" /> <com.android.systemui.dreams.DreamOverlayStatusBarView diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 2290964eccd0..39d7f4f5db4e 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -122,7 +122,7 @@ android:layout_marginTop="@dimen/notification_panel_margin_top" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" - android:layout_marginBottom="@dimen/close_handle_underlap" + android:layout_marginBottom="@dimen/notification_panel_margin_bottom" android:importantForAccessibility="no" systemui:layout_constraintStart_toStartOf="parent" systemui:layout_constraintEnd_toEndOf="parent" diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 89d046b83d97..c2cec5231243 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -26,6 +26,10 @@ keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp --> <dimen name="keyguard_clock_top_margin">8dp</dimen> + <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen> + + <dimen name="notification_panel_margin_bottom">48dp</dimen> + <!-- Limit the TaskView to this percentage of the overall screen width (0.0 - 1.0) --> <item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item> <dimen name="controls_task_view_right_margin">8dp</dimen> diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000000..219fd43c0e83 --- /dev/null +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +--> +<resources> + <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen> + <dimen name="notification_panel_margin_bottom">56dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3cceaf04f035..74bb9e45a6f2 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -311,9 +311,6 @@ <!-- Move the back button drawable for 3 button layout upwards in ime mode and in portrait --> <dimen name="navbar_back_button_ime_offset">2dp</dimen> - <!-- Amount of close_handle that will NOT overlap the notification list --> - <dimen name="close_handle_underlap">32dp</dimen> - <!-- Height of the status bar header bar in the car setting. --> <dimen name="car_status_bar_header_height">128dp</dimen> @@ -376,8 +373,12 @@ --> <dimen name="nssl_split_shade_min_content_height">256dp</dimen> - <!-- The bottom margin of the panel that holds the list of notifications. --> - <dimen name="notification_panel_margin_bottom">0dp</dimen> + <dimen name="notification_panel_margin_bottom">32dp</dimen> + + <!-- The bottom padding of the panel that holds the list of notifications. --> + <dimen name="notification_panel_padding_bottom">0dp</dimen> + + <dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen> <dimen name="notification_panel_width">@dimen/match_parent</dimen> @@ -1345,6 +1346,16 @@ shade. --> <dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen> + <!-- Dream overlay complications related dimensions --> + <dimen name="dream_overlay_complication_clock_time_padding_left">50dp</dimen> + <dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen> + <dimen name="dream_overlay_complication_clock_date_padding_left">60dp</dimen> + <dimen name="dream_overlay_complication_clock_date_padding_bottom">50dp</dimen> + <dimen name="dream_overlay_complication_clock_date_text_size">18sp</dimen> + <dimen name="dream_overlay_complication_weather_padding_left">20dp</dimen> + <dimen name="dream_overlay_complication_weather_padding_bottom">50dp</dimen> + <dimen name="dream_overlay_complication_weather_text_size">18sp</dimen> + <!-- The position of the end guide, which dream overlay complications can align their start with if their end is aligned with the parent end. Represented as the percentage over from the start of the parent container. --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 9b238296418f..590cc9b4eb0a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -236,6 +236,41 @@ <item name="android:textColor">?android:attr/colorError</item> </style> + <style name="TextAppearance.AuthNonBioCredential" + parent="@android:style/TextAppearance.DeviceDefault"> + <item name="android:accessibilityLiveRegion">polite</item> + <item name="android:textAlignment">gravity</item> + <item name="android:layout_gravity">top</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="TextAppearance.AuthNonBioCredential.Title"> + <item name="android:fontFamily">google-sans</item> + <item name="android:layout_marginTop">20dp</item> + <item name="android:textSize">36sp</item> + </style> + + <style name="TextAppearance.AuthNonBioCredential.Subtitle"> + <item name="android:fontFamily">google-sans</item> + <item name="android:layout_marginTop">20dp</item> + <item name="android:textSize">18sp</item> + </style> + + <style name="TextAppearance.AuthNonBioCredential.Description"> + <item name="android:fontFamily">google-sans</item> + <item name="android:layout_marginTop">20dp</item> + <item name="android:textSize">16sp</item> + </style> + + <style name="TextAppearance.AuthNonBioCredential.Error"> + <item name="android:paddingTop">6dp</item> + <item name="android:paddingBottom">18dp</item> + <item name="android:paddingHorizontal">24dp</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?android:attr/colorError</item> + <item name="android:gravity">center</item> + </style> + <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:gravity">center</item> <item name="android:singleLine">true</item> @@ -243,6 +278,15 @@ <item name="android:textSize">24sp</item> </style> + <style name="AuthCredentialHeaderStyle"> + <item name="android:paddingStart">48dp</item> + <item name="android:paddingEnd">24dp</item> + <item name="android:paddingTop">28dp</item> + <item name="android:paddingBottom">20dp</item> + <item name="android:orientation">vertical</item> + <item name="android:layout_gravity">top</item> + </style> + <style name="DeviceManagementDialogTitle"> <item name="android:gravity">center</item> <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item> @@ -307,9 +351,8 @@ <item name="android:maxWidth">420dp</item> <item name="android:minHeight">0dp</item> <item name="android:minWidth">0dp</item> - <item name="android:paddingBottom">0dp</item> - <item name="android:paddingHorizontal">44dp</item> - <item name="android:paddingTop">0dp</item> + <item name="android:paddingHorizontal">60dp</item> + <item name="android:paddingBottom">40dp</item> </style> <style name="LockPatternStyle"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt index 9010d5154156..fc6bb500562e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt @@ -42,7 +42,9 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor( * are different than actual bounds (e.g. view container may * have larger width than width of the items in the container) */ - private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {} + private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {}, + /** Allows to set the alpha based on the progress. */ + private val alphaProvider: AlphaProvider? = null ) : UnfoldTransitionProgressProvider.TransitionProgressListener { private val screenSize = Point() @@ -99,17 +101,27 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor( override fun onTransitionProgress(progress: Float) { animatedViews.forEach { - it.view.get()?.let { view -> - translationApplier.apply( - view = view, - x = it.startTranslationX * (1 - progress), - y = it.startTranslationY * (1 - progress) - ) - } + it.applyTransition(progress) + it.applyAlpha(progress) } lastAnimationProgress = progress } + private fun AnimatedView.applyTransition(progress: Float) { + view.get()?.let { view -> + translationApplier.apply( + view = view, + x = startTranslationX * (1 - progress), + y = startTranslationY * (1 - progress) + ) + } + } + + private fun AnimatedView.applyAlpha(progress: Float) { + if (alphaProvider == null) return + view.get()?.alpha = alphaProvider.getAlpha(progress) + } + private fun createAnimatedView(view: View): AnimatedView = AnimatedView(view = WeakReference(view)).updateAnimatedView(view) @@ -146,6 +158,13 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor( } } + /** Allows to set a custom alpha based on the progress. */ + interface AlphaProvider { + + /** Returns the alpha views should have at a given progress. */ + fun getAlpha(progress: Float): Float + } + /** * Interface that allows to use custom logic to get the center of the view */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl new file mode 100644 index 000000000000..6db06f046c55 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.media; + +import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback; +import com.android.systemui.shared.media.NearbyDevice; + +/** + * An interface that provides information about nearby devices that are able to play media. + * + * External clients will implement this interface and System UI will invoke it if it's passed to + * SystemUI via {@link INearbyMediaDevicesService.registerProvider}. + */ +interface INearbyMediaDevicesProvider { + /** + * Returns a list of nearby devices that are able to play media. + */ + List<NearbyDevice> getCurrentNearbyDevices() = 1; + + /** + * Registers a callback that will be notified each time the status of a nearby device changes. + */ + oneway void registerNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 2; + + /** + * Unregisters a callback. See {@link registerNearbyDevicesCallback}. + */ + oneway void unregisterNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 3; +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl new file mode 100644 index 000000000000..4f3e10d801f0 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.media; + +import com.android.systemui.shared.media.INearbyMediaDevicesProvider; + +/** + * An interface that can be invoked to notify System UI of nearby media devices. + * + * External clients wanting to notify System UI about the status of nearby media devices should + * implement {@link INearbyMediaDevicesProvider} and then register it with system UI using this + * service. + * + * System UI will implement this interface and external clients will invoke it. + */ +interface INearbyMediaDevicesService { + /** Registers a new provider. */ + oneway void registerProvider(INearbyMediaDevicesProvider provider) = 1; +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl new file mode 100644 index 000000000000..a835f5227852 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.media; + +/** + * A callback used to notify implementors of changes in the status of nearby devices that are able + * to play media. + * + * External clients may allow registration of these callbacks and external clients will be + * responsible for notifying the callbacks appropriately. System UI is only a mediator between the + * external client and these callbacks. + */ +interface INearbyMediaDevicesUpdateCallback { + /** Unknown distance range. */ + const int RANGE_UNKNOWN = 0; + /** Distance is very far away from the peer device. */ + const int RANGE_FAR = 1; + /** Distance is relatively long from the peer device, typically a few meters. */ + const int RANGE_LONG = 2; + /** Distance is close to the peer device, typically with one or two meter. */ + const int RANGE_CLOSE = 3; + /** Distance is very close to the peer device, typically within one meter or less. */ + const int RANGE_WITHIN_REACH = 4; + + /** Invoked by external clients when media device changes are detected. */ + oneway void nearbyDeviceUpdate(in String routeId, in int rangeZone) = 1; +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl new file mode 100644 index 000000000000..62b50ed5f953 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.media; + +parcelable NearbyDevice; diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt index 96b853f394d4..9cab3ab355dd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.media.nearby +package com.android.systemui.shared.media import android.os.Parcel import android.os.Parcelable @@ -26,14 +26,15 @@ import android.os.Parcelable * - [routeId] identifying the media route * - [rangeZone] specifying how far away the device with the media route is from this device. */ -class NearbyDevice(parcel: Parcel) : Parcelable { - var routeId: String? = null +class NearbyDevice( + val routeId: String?, @RangeZone val rangeZone: Int +) : Parcelable { - init { - routeId = parcel.readString() ?: "unknown" + private constructor(parcel: Parcel) : this( + routeId = parcel.readString() ?: null, rangeZone = parcel.readInt() - } + ) override fun describeContents() = 0 diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt index 3c890bc9efab..b5eaff6e1cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt @@ -14,31 +14,18 @@ * limitations under the License. */ -package com.android.systemui.media.nearby +package com.android.systemui.shared.media import androidx.annotation.IntDef import kotlin.annotation.AnnotationRetention @IntDef( - RangeZone.RANGE_UNKNOWN, - RangeZone.RANGE_FAR, - RangeZone.RANGE_LONG, - RangeZone.RANGE_CLOSE, - RangeZone.RANGE_WITHIN_REACH + INearbyMediaDevicesUpdateCallback.RANGE_UNKNOWN, + INearbyMediaDevicesUpdateCallback.RANGE_FAR, + INearbyMediaDevicesUpdateCallback.RANGE_LONG, + INearbyMediaDevicesUpdateCallback.RANGE_CLOSE, + INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH ) @Retention(AnnotationRetention.SOURCE) /** The various range zones a device can be in, in relation to the current device. */ -annotation class RangeZone { - companion object { - /** Unknown distance range. */ - const val RANGE_UNKNOWN = 0 - /** Distance is very far away from the peer device. */ - const val RANGE_FAR = 1 - /** Distance is relatively long from the peer device, typically a few meters. */ - const val RANGE_LONG = 2 - /** Distance is close to the peer device, typically with one or two meter. */ - const val RANGE_CLOSE = 3 - /** Distance is very close to the peer device, typically within one meter or less. */ - const val RANGE_WITHIN_REACH = 4 - } -} +annotation class RangeZone diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 20d6e3247cd1..881e6a917a45 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -120,6 +120,13 @@ public class SystemActions extends CoreStartable { AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9 /** + * Action ID to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and + * play/stop media + */ + private static final int SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK = + AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK; // = 10 + + /** * Action ID to trigger the accessibility button */ public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON = @@ -137,6 +144,36 @@ public class SystemActions extends CoreStartable { public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE = AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15 + /** + * Action ID to trigger the dpad up button + */ + private static final int SYSTEM_ACTION_ID_DPAD_UP = + AccessibilityService.GLOBAL_ACTION_DPAD_UP; // 16 + + /** + * Action ID to trigger the dpad down button + */ + private static final int SYSTEM_ACTION_ID_DPAD_DOWN = + AccessibilityService.GLOBAL_ACTION_DPAD_DOWN; // 17 + + /** + * Action ID to trigger the dpad left button + */ + private static final int SYSTEM_ACTION_ID_DPAD_LEFT = + AccessibilityService.GLOBAL_ACTION_DPAD_LEFT; // 18 + + /** + * Action ID to trigger the dpad right button + */ + private static final int SYSTEM_ACTION_ID_DPAD_RIGHT = + AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT; // 19 + + /** + * Action ID to trigger dpad center keyevent + */ + private static final int SYSTEM_ACTION_ID_DPAD_CENTER = + AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20 + private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; private final SystemActionsBroadcastReceiver mReceiver; @@ -222,10 +259,34 @@ public class SystemActions extends CoreStartable { R.string.accessibility_system_action_screenshot_label, SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT); + RemoteAction actionHeadsetHook = createRemoteAction( + R.string.accessibility_system_action_headset_hook_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK); + RemoteAction actionAccessibilityShortcut = createRemoteAction( R.string.accessibility_system_action_hardware_a11y_shortcut_label, SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT); + RemoteAction actionDpadUp = createRemoteAction( + R.string.accessibility_system_action_dpad_up_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP); + + RemoteAction actionDpadDown = createRemoteAction( + R.string.accessibility_system_action_dpad_down_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN); + + RemoteAction actionDpadLeft = createRemoteAction( + R.string.accessibility_system_action_dpad_left_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT); + + RemoteAction actionDpadRight = createRemoteAction( + R.string.accessibility_system_action_dpad_right_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT); + + RemoteAction actionDpadCenter = createRemoteAction( + R.string.accessibility_system_action_dpad_center_label, + SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER); + mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK); mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME); mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS); @@ -234,8 +295,14 @@ public class SystemActions extends CoreStartable { mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG); mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN); mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT); + mA11yManager.registerSystemAction(actionHeadsetHook, SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK); mA11yManager.registerSystemAction( actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT); + mA11yManager.registerSystemAction(actionDpadUp, SYSTEM_ACTION_ID_DPAD_UP); + mA11yManager.registerSystemAction(actionDpadDown, SYSTEM_ACTION_ID_DPAD_DOWN); + mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT); + mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT); + mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER); registerOrUnregisterDismissNotificationShadeAction(); } @@ -305,6 +372,10 @@ public class SystemActions extends CoreStartable { labelId = R.string.accessibility_system_action_screenshot_label; intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT; break; + case SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK: + labelId = R.string.accessibility_system_action_headset_hook_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK; + break; case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON: labelId = R.string.accessibility_system_action_on_screen_a11y_shortcut_label; intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON; @@ -323,6 +394,26 @@ public class SystemActions extends CoreStartable { intent = SystemActionsBroadcastReceiver .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE; break; + case SYSTEM_ACTION_ID_DPAD_UP: + labelId = R.string.accessibility_system_action_dpad_up_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP; + break; + case SYSTEM_ACTION_ID_DPAD_DOWN: + labelId = R.string.accessibility_system_action_dpad_down_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN; + break; + case SYSTEM_ACTION_ID_DPAD_LEFT: + labelId = R.string.accessibility_system_action_dpad_left_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT; + break; + case SYSTEM_ACTION_ID_DPAD_RIGHT: + labelId = R.string.accessibility_system_action_dpad_right_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT; + break; + case SYSTEM_ACTION_ID_DPAD_CENTER: + labelId = R.string.accessibility_system_action_dpad_center_label; + intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER; + break; default: return; } @@ -411,6 +502,10 @@ public class SystemActions extends CoreStartable { SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null); } + private void handleHeadsetHook() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK); + } + private void handleAccessibilityButton() { AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked( Display.DEFAULT_DISPLAY); @@ -434,6 +529,26 @@ public class SystemActions extends CoreStartable { CommandQueue.FLAG_EXCLUDE_NONE, false /* force */)); } + private void handleDpadUp() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP); + } + + private void handleDpadDown() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN); + } + + private void handleDpadLeft() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); + } + + private void handleDpadRight() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT); + } + + private void handleDpadCenter() { + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER); + } + private class SystemActionsBroadcastReceiver extends BroadcastReceiver { private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK"; private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME"; @@ -443,6 +558,7 @@ public class SystemActions extends CoreStartable { private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG"; private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN"; private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT"; + private static final String INTENT_ACTION_HEADSET_HOOK = "SYSTEM_ACTION_HEADSET_HOOK"; private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON = "SYSTEM_ACTION_ACCESSIBILITY_BUTTON"; private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = @@ -451,6 +567,11 @@ public class SystemActions extends CoreStartable { "SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT"; private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE = "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE"; + private static final String INTENT_ACTION_DPAD_UP = "SYSTEM_ACTION_DPAD_UP"; + private static final String INTENT_ACTION_DPAD_DOWN = "SYSTEM_ACTION_DPAD_DOWN"; + private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT"; + private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT"; + private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER"; private PendingIntent createPendingIntent(Context context, String intentAction) { switch (intentAction) { @@ -462,10 +583,16 @@ public class SystemActions extends CoreStartable { case INTENT_ACTION_POWER_DIALOG: case INTENT_ACTION_LOCK_SCREEN: case INTENT_ACTION_TAKE_SCREENSHOT: + case INTENT_ACTION_HEADSET_HOOK: case INTENT_ACTION_ACCESSIBILITY_BUTTON: case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: - case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: { + case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: + case INTENT_ACTION_DPAD_UP: + case INTENT_ACTION_DPAD_DOWN: + case INTENT_ACTION_DPAD_LEFT: + case INTENT_ACTION_DPAD_RIGHT: + case INTENT_ACTION_DPAD_CENTER: { Intent intent = new Intent(intentAction); intent.setPackage(context.getPackageName()); return PendingIntent.getBroadcast(context, 0, intent, @@ -487,10 +614,16 @@ public class SystemActions extends CoreStartable { intentFilter.addAction(INTENT_ACTION_POWER_DIALOG); intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN); intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT); + intentFilter.addAction(INTENT_ACTION_HEADSET_HOOK); intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON); intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER); intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT); intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE); + intentFilter.addAction(INTENT_ACTION_DPAD_UP); + intentFilter.addAction(INTENT_ACTION_DPAD_DOWN); + intentFilter.addAction(INTENT_ACTION_DPAD_LEFT); + intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT); + intentFilter.addAction(INTENT_ACTION_DPAD_CENTER); return intentFilter; } @@ -530,6 +663,10 @@ public class SystemActions extends CoreStartable { handleTakeScreenshot(); break; } + case INTENT_ACTION_HEADSET_HOOK: { + handleHeadsetHook(); + break; + } case INTENT_ACTION_ACCESSIBILITY_BUTTON: { handleAccessibilityButton(); break; @@ -546,6 +683,26 @@ public class SystemActions extends CoreStartable { handleAccessibilityDismissNotificationShade(); break; } + case INTENT_ACTION_DPAD_UP: { + handleDpadUp(); + break; + } + case INTENT_ACTION_DPAD_DOWN: { + handleDpadDown(); + break; + } + case INTENT_ACTION_DPAD_LEFT: { + handleDpadLeft(); + break; + } + case INTENT_ACTION_DPAD_RIGHT: { + handleDpadRight(); + break; + } + case INTENT_ACTION_DPAD_CENTER: { + handleDpadCenter(); + break; + } default: break; } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 41287b60fbf8..ec2beb15959e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -27,6 +27,9 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.clipboardoverlay.ClipboardListener; import com.android.systemui.dreams.DreamOverlayRegistrant; import com.android.systemui.dreams.SmartSpaceComplication; +import com.android.systemui.dreams.complication.DreamClockDateComplication; +import com.android.systemui.dreams.complication.DreamClockTimeComplication; +import com.android.systemui.dreams.complication.DreamWeatherComplication; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; @@ -234,4 +237,25 @@ public abstract class SystemUIBinder { @ClassKey(MediaDreamSentinel.class) public abstract CoreStartable bindMediaDreamSentinel( MediaDreamSentinel sentinel); + + /** Inject into DreamClockTimeComplication.Registrant */ + @Binds + @IntoMap + @ClassKey(DreamClockTimeComplication.Registrant.class) + public abstract CoreStartable bindDreamClockTimeComplicationRegistrant( + DreamClockTimeComplication.Registrant registrant); + + /** Inject into DreamClockDateComplication.Registrant */ + @Binds + @IntoMap + @ClassKey(DreamClockDateComplication.Registrant.class) + public abstract CoreStartable bindDreamClockDateComplicationRegistrant( + DreamClockDateComplication.Registrant registrant); + + /** Inject into DreamWeatherComplication.Registrant */ + @Binds + @IntoMap + @ClassKey(DreamWeatherComplication.Registrant.class) + public abstract CoreStartable bindDreamWeatherComplicationRegistrant( + DreamWeatherComplication.Registrant registrant); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java new file mode 100644 index 000000000000..59c52b9e402b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication; + +import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_VIEW; + +import android.content.Context; +import android.view.View; + +import com.android.systemui.CoreStartable; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Clock Date Complication that produce Clock Date view holder. + */ +public class DreamClockDateComplication implements Complication { + DreamClockDateComplicationComponent.Factory mComponentFactory; + + /** + * Default constructor for {@link DreamClockDateComplication}. + */ + @Inject + public DreamClockDateComplication( + DreamClockDateComplicationComponent.Factory componentFactory) { + mComponentFactory = componentFactory; + } + + /** + * Create {@link DreamClockDateViewHolder}. + */ + @Override + public ViewHolder createView(ComplicationViewModel model) { + return mComponentFactory.create().getViewHolder(); + } + + /** + * {@link CoreStartable} responsbile for registering {@link DreamClockDateComplication} with + * SystemUI. + */ + public static class Registrant extends CoreStartable { + private final DreamOverlayStateController mDreamOverlayStateController; + private final DreamClockDateComplication mComplication; + + /** + * Default constructor to register {@link DreamClockDateComplication}. + */ + @Inject + public Registrant(Context context, + DreamOverlayStateController dreamOverlayStateController, + DreamClockDateComplication dreamClockDateComplication) { + super(context); + mDreamOverlayStateController = dreamOverlayStateController; + mComplication = dreamClockDateComplication; + } + + @Override + public void start() { + mDreamOverlayStateController.addComplication(mComplication); + } + } + + /** + * ViewHolder to contain value/logic associated with a Clock Date Complication View. + */ + public static class DreamClockDateViewHolder implements ViewHolder { + private final View mView; + private final ComplicationLayoutParams mLayoutParams; + + @Inject + DreamClockDateViewHolder(@Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) View view, + @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS) + ComplicationLayoutParams layoutParams) { + mView = view; + mLayoutParams = layoutParams; + } + + @Override + public View getView() { + return mView; + } + + @Override + public ComplicationLayoutParams getLayoutParams() { + return mLayoutParams; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java new file mode 100644 index 000000000000..b0c3a424cc92 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication; + +import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW; + +import android.content.Context; +import android.view.View; + +import com.android.systemui.CoreStartable; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Clock Time Complication that produce Clock Time view holder. + */ +public class DreamClockTimeComplication implements Complication { + DreamClockTimeComplicationComponent.Factory mComponentFactory; + + /** + * Default constructor for {@link DreamClockTimeComplication}. + */ + @Inject + public DreamClockTimeComplication( + DreamClockTimeComplicationComponent.Factory componentFactory) { + mComponentFactory = componentFactory; + } + + /** + * Create {@link DreamClockTimeViewHolder}. + */ + @Override + public ViewHolder createView(ComplicationViewModel model) { + return mComponentFactory.create().getViewHolder(); + } + + /** + * {@link CoreStartable} responsbile for registering {@link DreamClockTimeComplication} with + * SystemUI. + */ + public static class Registrant extends CoreStartable { + private final DreamOverlayStateController mDreamOverlayStateController; + private final DreamClockTimeComplication mComplication; + + /** + * Default constructor to register {@link DreamClockTimeComplication}. + */ + @Inject + public Registrant(Context context, + DreamOverlayStateController dreamOverlayStateController, + DreamClockTimeComplication dreamClockTimeComplication) { + super(context); + mDreamOverlayStateController = dreamOverlayStateController; + mComplication = dreamClockTimeComplication; + } + + @Override + public void start() { + mDreamOverlayStateController.addComplication(mComplication); + } + } + + /** + * ViewHolder to contain value/logic associated with a Clock Time Complication View. + */ + public static class DreamClockTimeViewHolder implements ViewHolder { + private final View mView; + private final ComplicationLayoutParams mLayoutParams; + + @Inject + DreamClockTimeViewHolder(@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view, + @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS) + ComplicationLayoutParams layoutParams) { + mView = view; + mLayoutParams = layoutParams; + } + + @Override + public View getView() { + return mView; + } + + @Override + public ComplicationLayoutParams getLayoutParams() { + return mLayoutParams; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java new file mode 100644 index 000000000000..cbdbef3ae57e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication; + +import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW; + +import android.app.smartspace.SmartspaceAction; +import android.app.smartspace.SmartspaceTarget; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.text.TextUtils; +import android.widget.TextView; + +import com.android.systemui.CoreStartable; +import com.android.systemui.R; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent; +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Weather Complication that produce Weather view holder. + */ +public class DreamWeatherComplication implements Complication { + DreamWeatherComplicationComponent.Factory mComponentFactory; + + /** + * Default constructor for {@link DreamWeatherComplication}. + */ + @Inject + public DreamWeatherComplication( + DreamWeatherComplicationComponent.Factory componentFactory) { + mComponentFactory = componentFactory; + } + + /** + * Create {@link DreamWeatherViewHolder}. + */ + @Override + public ViewHolder createView(ComplicationViewModel model) { + return mComponentFactory.create().getViewHolder(); + } + + /** + * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI. + */ + public static class Registrant extends CoreStartable { + private final LockscreenSmartspaceController mSmartSpaceController; + private final DreamOverlayStateController mDreamOverlayStateController; + private final DreamWeatherComplication mComplication; + + /** + * Default constructor to register {@link DreamWeatherComplication}. + */ + @Inject + public Registrant(Context context, + LockscreenSmartspaceController smartspaceController, + DreamOverlayStateController dreamOverlayStateController, + DreamWeatherComplication dreamWeatherComplication) { + super(context); + mSmartSpaceController = smartspaceController; + mDreamOverlayStateController = dreamOverlayStateController; + mComplication = dreamWeatherComplication; + } + + @Override + public void start() { + if (mSmartSpaceController.isEnabled()) { + mDreamOverlayStateController.addComplication(mComplication); + } + } + } + + /** + * ViewHolder to contain value/logic associated with a Weather Complication View. + */ + public static class DreamWeatherViewHolder implements ViewHolder { + private final TextView mView; + private final ComplicationLayoutParams mLayoutParams; + private final DreamWeatherViewController mViewController; + + @Inject + DreamWeatherViewHolder( + @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, + DreamWeatherViewController controller, + @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS) + ComplicationLayoutParams layoutParams) { + mView = view; + mLayoutParams = layoutParams; + mViewController = controller; + mViewController.init(); + } + + @Override + public TextView getView() { + return mView; + } + + @Override + public ComplicationLayoutParams getLayoutParams() { + return mLayoutParams; + } + } + + /** + * ViewController to contain value/logic associated with a Weather Complication View. + */ + static class DreamWeatherViewController extends ViewController<TextView> { + private final LockscreenSmartspaceController mSmartSpaceController; + private SmartspaceTargetListener mSmartspaceTargetListener; + + @Inject + DreamWeatherViewController( + @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, + LockscreenSmartspaceController smartspaceController + ) { + super(view); + mSmartSpaceController = smartspaceController; + } + + @Override + protected void onViewAttached() { + mSmartspaceTargetListener = targets -> targets.forEach( + t -> { + if (t instanceof SmartspaceTarget + && ((SmartspaceTarget) t).getFeatureType() + == SmartspaceTarget.FEATURE_WEATHER) { + final SmartspaceTarget target = (SmartspaceTarget) t; + final SmartspaceAction headerAction = target.getHeaderAction(); + if (headerAction == null || TextUtils.isEmpty( + headerAction.getTitle())) { + return; + } + + String temperature = headerAction.getTitle().toString(); + mView.setText(temperature); + final Icon icon = headerAction.getIcon(); + if (icon != null) { + final int iconSize = + getResources().getDimensionPixelSize( + R.dimen.smart_action_button_icon_size); + final Drawable iconDrawable = icon.loadDrawable(getContext()); + iconDrawable.setBounds(0, 0, iconSize, iconSize); + mView.setCompoundDrawables(iconDrawable, null, null, null); + mView.setCompoundDrawablePadding( + getResources().getDimensionPixelSize( + R.dimen.smart_action_button_icon_padding)); + + } + } + }); + mSmartSpaceController.addListener(mSmartspaceTargetListener); + } + + @Override + protected void onViewDetached() { + mSmartSpaceController.removeListener(mSmartspaceTargetListener); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java new file mode 100644 index 000000000000..eaffb1ca5b3e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.DreamClockDateComplication.DreamClockDateViewHolder; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Named; +import javax.inject.Scope; + +import dagger.Module; +import dagger.Provides; +import dagger.Subcomponent; + +/** + * {@link DreamClockDateComplicationComponent} is responsible for generating dependencies + * surrounding the + * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout + * details. + */ +@Subcomponent(modules = { + DreamClockDateComplicationComponent.DreamClockDateComplicationModule.class, +}) +@DreamClockDateComplicationComponent.DreamClockDateComplicationScope +public interface DreamClockDateComplicationComponent { + /** + * Creates {@link DreamClockDateViewHolder}. + */ + DreamClockDateViewHolder getViewHolder(); + + @Documented + @Retention(RUNTIME) + @Scope + @interface DreamClockDateComplicationScope { + } + + /** + * Generates {@link DreamClockDateComplicationComponent}. + */ + @Subcomponent.Factory + interface Factory { + DreamClockDateComplicationComponent create(); + } + + /** + * Scoped values for {@link DreamClockDateComplicationComponent}. + */ + @Module + interface DreamClockDateComplicationModule { + String DREAM_CLOCK_DATE_COMPLICATION_VIEW = "clock_date_complication_view"; + String DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS = + "clock_date_complication_layout_params"; + // Order weight of insert into parent container + int INSERT_ORDER_WEIGHT = 2; + + /** + * Provides the complication view. + */ + @Provides + @DreamClockDateComplicationScope + @Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) + static View provideComplicationView(LayoutInflater layoutInflater) { + return Preconditions.checkNotNull( + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_date, + null, false), + "R.layout.dream_overlay_complication_clock_date did not properly inflated"); + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @DreamClockDateComplicationScope + @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_BOTTOM + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_END, + INSERT_ORDER_WEIGHT, + true); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java new file mode 100644 index 000000000000..053c5b345760 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.DreamClockTimeComplication.DreamClockTimeViewHolder; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Named; +import javax.inject.Scope; + +import dagger.Module; +import dagger.Provides; +import dagger.Subcomponent; + +/** + * {@link DreamClockTimeComplicationComponent} is responsible for generating dependencies + * surrounding the + * Clock Time {@link com.android.systemui.dreams.complication.Complication}, such as the layout + * details. + */ +@Subcomponent(modules = { + DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.class, +}) +@DreamClockTimeComplicationComponent.DreamClockTimeComplicationScope +public interface DreamClockTimeComplicationComponent { + /** + * Creates {@link DreamClockTimeViewHolder}. + */ + DreamClockTimeViewHolder getViewHolder(); + + @Documented + @Retention(RUNTIME) + @Scope + @interface DreamClockTimeComplicationScope { + } + + /** + * Generates {@link DreamClockTimeComplicationComponent}. + */ + @Subcomponent.Factory + interface Factory { + DreamClockTimeComplicationComponent create(); + } + + /** + * Scoped values for {@link DreamClockTimeComplicationComponent}. + */ + @Module + interface DreamClockTimeComplicationModule { + String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view"; + String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS = + "clock_time_complication_layout_params"; + // Order weight of insert into parent container + int INSERT_ORDER_WEIGHT = 0; + + /** + * Provides the complication view. + */ + @Provides + @DreamClockTimeComplicationScope + @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) + static View provideComplicationView(LayoutInflater layoutInflater) { + return Preconditions.checkNotNull( + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, + null, false), + "R.layout.dream_overlay_complication_clock_time did not properly inflated"); + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @DreamClockTimeComplicationScope + @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_BOTTOM + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_UP, + INSERT_ORDER_WEIGHT, + true); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java new file mode 100644 index 000000000000..a282594ff282 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.DreamWeatherComplication.DreamWeatherViewHolder; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Named; +import javax.inject.Scope; + +import dagger.Module; +import dagger.Provides; +import dagger.Subcomponent; + +/** + * {@link DreamWeatherComplicationComponent} is responsible for generating dependencies surrounding + * the + * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout + * details. + */ +@Subcomponent(modules = { + DreamWeatherComplicationComponent.DreamWeatherComplicationModule.class, +}) +@DreamWeatherComplicationComponent.DreamWeatherComplicationScope +public interface DreamWeatherComplicationComponent { + /** + * Creates {@link DreamWeatherViewHolder}. + */ + DreamWeatherViewHolder getViewHolder(); + + @Documented + @Retention(RUNTIME) + @Scope + @interface DreamWeatherComplicationScope { + } + + /** + * Generates {@link DreamWeatherComplicationComponent}. + */ + @Subcomponent.Factory + interface Factory { + DreamWeatherComplicationComponent create(); + } + + /** + * Scoped values for {@link DreamWeatherComplicationComponent}. + */ + @Module + interface DreamWeatherComplicationModule { + String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view"; + String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS = + "weather_complication_layout_params"; + // Order weight of insert into parent container + int INSERT_ORDER_WEIGHT = 1; + + /** + * Provides the complication view. + */ + @Provides + @DreamWeatherComplicationScope + @Named(DREAM_WEATHER_COMPLICATION_VIEW) + static TextView provideComplicationView(LayoutInflater layoutInflater) { + return Preconditions.checkNotNull((TextView) + layoutInflater.inflate(R.layout.dream_overlay_complication_weather, + null, false), + "R.layout.dream_overlay_complication_weather did not properly inflated"); + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @DreamWeatherComplicationScope + @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_BOTTOM + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_END, + INSERT_ORDER_WEIGHT, + true); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java new file mode 100644 index 000000000000..8e4fb3783f4a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + +import com.android.systemui.dagger.SystemUIBinder; + +import dagger.Module; + +/** + * Module for all components with corresponding dream layer complications registered in + * {@link SystemUIBinder}. + */ +@Module(subcomponents = { + DreamClockTimeComplicationComponent.class, + DreamClockDateComplicationComponent.class, + DreamWeatherComplicationComponent.class, +}) +public interface RegisteredComplicationsModule { +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index 3d2f924563f3..d8af9e5f1f4a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -16,6 +16,7 @@ package com.android.systemui.dreams.dagger; +import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule; import com.android.systemui.dreams.touch.dagger.DreamTouchModule; import dagger.Module; @@ -25,6 +26,7 @@ import dagger.Module; */ @Module(includes = { DreamTouchModule.class, + RegisteredComplicationsModule.class, }, subcomponents = { DreamOverlayComponent.class, diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 97edf3bac4a8..c894b7023d75 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -112,6 +112,9 @@ public class Flags { public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS = new BooleanFlag(601, false); + public static final BooleanFlag STATUS_BAR_USER_SWITCHER = + new BooleanFlag(602, false); + /***************************************/ // 700 - dialer/calls public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP = diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index 44727f29888d..48f48266fda5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -159,8 +159,7 @@ class KeyguardMediaController @Inject constructor( } fun refreshMediaPosition() { - val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD || - statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER) + val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD) // mediaHost.visible required for proper animations handling visible = mediaHost.visible && !bypassController.bypassEnabled && @@ -196,4 +195,4 @@ class KeyguardMediaController @Inject constructor( visibilityChangedListener?.invoke(newVisibility == View.VISIBLE) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 64ebe568c790..6145f0ffebad 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -567,8 +567,7 @@ class MediaHierarchyManager @Inject constructor( previousLocation = this.desiredLocation } else if (forceStateUpdate) { val onLockscreen = (!bypassController.bypassEnabled && - (statusbarState == StatusBarState.KEYGUARD || - statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) + (statusbarState == StatusBarState.KEYGUARD)) if (desiredLocation == LOCATION_QS && previousLocation == LOCATION_LOCKSCREEN && !onLockscreen) { // If media active state changed and the device is now unlocked, update the @@ -955,8 +954,7 @@ class MediaHierarchyManager @Inject constructor( return desiredLocation } val onLockscreen = (!bypassController.bypassEnabled && - (statusbarState == StatusBarState.KEYGUARD || - statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) + (statusbarState == StatusBarState.KEYGUARD)) val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications() val location = when { dreamOverlayActive -> LOCATION_DREAM_OVERLAY @@ -1087,4 +1085,4 @@ private annotation class TransformationType @IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS, MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN]) @Retention(AnnotationRetention.SOURCE) -annotation class MediaLocation
\ No newline at end of file +annotation class MediaLocation diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 2bc910e4a21a..29938a05a327 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -26,6 +26,7 @@ import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.media.MediaHostStatesManager; import com.android.systemui.media.dream.dagger.MediaComplicationComponent; +import com.android.systemui.media.nearby.NearbyMediaDevicesService; import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver; @@ -142,4 +143,10 @@ public interface MediaModule { @IntoMap @ClassKey(MediaTttSenderService.class) Service bindMediaTttSenderService(MediaTttSenderService service); + + /** Inject into NearbyMediaDevicesService. */ + @Binds + @IntoMap + @ClassKey(NearbyMediaDevicesService.class) + Service bindMediaNearbyDevicesService(NearbyMediaDevicesService service); } diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt deleted file mode 100644 index 0453fdb45931..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.nearby - -import com.android.systemui.dagger.SysUISingleton - -/** - * A manager that returns information about devices that are nearby and can receive media transfers. - */ -@SysUISingleton -class MediaNearbyDevicesManager { - - /** Returns a list containing the current nearby devices. */ - fun getCurrentNearbyDevices(): List<NearbyDevice> { - // TODO(b/216313420): Implement this function. - return emptyList() - } - - /** - * Registers [callback] to be notified each time a device's range changes or when a new device - * comes within range. - */ - fun registerNearbyDevicesCallback( - callback: (device: NearbyDevice) -> Unit - ) { - // TODO(b/216313420): Implement this function. - } - - /** - * Un-registers [callback]. See [registerNearbyDevicesCallback]. - */ - fun unregisterNearbyDevicesCallback( - callback: (device: NearbyDevice) -> Unit - ) { - // TODO(b/216313420): Implement this function. - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt new file mode 100644 index 000000000000..eaf2bd96e37a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.nearby + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.shared.media.INearbyMediaDevicesProvider +import com.android.systemui.shared.media.INearbyMediaDevicesService +import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback +import com.android.systemui.shared.media.NearbyDevice +import javax.inject.Inject + +/** + * A service that acts as a bridge between (1) external clients that have data on nearby devices + * that are able to play media and (2) internal clients (like media Output Switcher) that need data + * on these nearby devices. + * + * TODO(b/216313420): Add logging to this class. + */ +@SysUISingleton +class NearbyMediaDevicesService @Inject constructor() : Service() { + + private var provider: INearbyMediaDevicesProvider? = null + + private val binder: IBinder = object : INearbyMediaDevicesService.Stub() { + override fun registerProvider(newProvider: INearbyMediaDevicesProvider) { + provider = newProvider + newProvider.asBinder().linkToDeath( + { + // We might've gotten a new provider before the old provider died, so we only + // need to clear our provider if the most recent provider died. + if (provider == newProvider) { + provider = null + } + }, + /* flags= */ 0 + ) + } + } + + override fun onBind(intent: Intent?): IBinder = binder + + /** Returns a list containing the current nearby devices. */ + fun getCurrentNearbyDevices(): List<NearbyDevice> { + val currentProvider = provider ?: return emptyList() + return currentProvider.currentNearbyDevices + } + + /** + * Registers [callback] to be notified each time a device's range changes or when a new device + * comes within range. + */ + fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) { + val currentProvider = provider ?: return + currentProvider.registerNearbyDevicesCallback(callback) + } + + /** + * Un-registers [callback]. See [registerNearbyDevicesCallback]. + */ + fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) { + val currentProvider = provider ?: return + currentProvider.unregisterNearbyDevicesCallback(callback) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java index c0148c0ecb21..16bc951e0323 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java @@ -37,12 +37,6 @@ public class StatusBarState { */ public static final int SHADE_LOCKED = 2; - /** - * Status bar is locked and shows the full screen user switcher. - */ - public static final int FULLSCREEN_USER_SWITCHER = 3; - - public static String toShortString(int x) { switch (x) { case SHADE: @@ -51,8 +45,6 @@ public class StatusBarState { return "SHD_LCK"; case KEYGUARD: return "KGRD"; - case FULLSCREEN_USER_SWITCHER: - return "FS_USRSW"; default: return "bad_value_" + x; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index f56602ee2bcd..ee12cc5679fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -76,7 +76,7 @@ public class StatusBarStateControllerImpl implements // Must be a power of 2 private static final int HISTORY_SIZE = 32; - private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER; + private static final int MAX_STATE = StatusBarState.SHADE_LOCKED; private static final int MIN_STATE = StatusBarState.SHADE; private static final Comparator<RankedListener> sComparator = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java index 8330169980d4..b66a48e07777 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java @@ -34,10 +34,7 @@ public enum StatusBarStateEvent implements UiEventLogger.UiEventEnum { STATUS_BAR_STATE_KEYGUARD(430), @UiEvent(doc = "StatusBarState changed to SHADE_LOCKED state") - STATUS_BAR_STATE_SHADE_LOCKED(431), - - @UiEvent(doc = "StatusBarState changed to FULLSCREEN_USER_SWITCHER state") - STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER(432); + STATUS_BAR_STATE_SHADE_LOCKED(431); private int mId; StatusBarStateEvent(int id) { @@ -60,8 +57,6 @@ public enum StatusBarStateEvent implements UiEventLogger.UiEventEnum { return STATUS_BAR_STATE_KEYGUARD; case StatusBarState.SHADE_LOCKED: return STATUS_BAR_STATE_SHADE_LOCKED; - case StatusBarState.FULLSCREEN_USER_SWITCHER: - return STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER; default: return STATUS_BAR_STATE_UNKNOWN; } 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 9f6a81d3a9ee..dd72615768b0 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 @@ -199,7 +199,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mMaxTopPadding; private int mTopPadding; private boolean mAnimateNextTopPaddingChange; - private int mBottomMargin; + private int mBottomPadding; private int mBottomInset = 0; private float mQsExpansionFraction; private final int mSplitShadeMinContentHeight; @@ -981,7 +981,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mMinTopOverScrollToEscape = res.getDimensionPixelSize( R.dimen.min_top_overscroll_to_qs); mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext); - mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom); + mBottomPadding = res.getDimensionPixelSize(R.dimen.notification_panel_padding_bottom); mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings); mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal); mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape); @@ -2280,7 +2280,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // The topPadding can be bigger than the regular padding when qs is expanded, in that // state the maxPanelHeight and the contentHeight should be bigger - mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin; + mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomPadding; updateScrollability(); clampScrollPosition(); updateStackPosition(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 8f0579cc4693..e24cd3e9b016 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -46,7 +46,7 @@ import java.util.List; */ public class StackScrollAlgorithm { - public static final float START_FRACTION = 0.3f; + public static final float START_FRACTION = 0.5f; private static final String LOG_TAG = "StackScrollAlgorithm"; private final ViewGroup mHostView; @@ -61,7 +61,7 @@ public class StackScrollAlgorithm { @VisibleForTesting float mHeadsUpInset; private int mPinnedZTranslationExtra; private float mNotificationScrimPadding; - private int mCloseHandleUnderlapHeight; + private int mMarginBottom; public StackScrollAlgorithm( Context context, @@ -87,7 +87,7 @@ public class StackScrollAlgorithm { R.dimen.heads_up_pinned_elevation); mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height); mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings); - mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap); + mMarginBottom = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom); } /** @@ -463,7 +463,7 @@ public class StackScrollAlgorithm { } } else { if (view instanceof EmptyShadeView) { - float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight + float fullHeight = ambientState.getLayoutMaxHeight() + mMarginBottom - ambientState.getStackY(); viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f; } else if (view != ambientState.getTrackedHeadsUpRow()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 04d3e9a01d86..6a78370c7dab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -180,6 +180,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final MetricsLogger mMetricsLogger; private final AuthController mAuthController; private final StatusBarStateController mStatusBarStateController; + private final LatencyTracker mLatencyTracker; private long mLastFpFailureUptimeMillis; private int mNumConsecutiveFpFailures; @@ -281,7 +282,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp AuthController authController, StatusBarStateController statusBarStateController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - SessionTracker sessionTracker) { + SessionTracker sessionTracker, + LatencyTracker latencyTracker) { mContext = context; mPowerManager = powerManager; mShadeController = shadeController; @@ -289,6 +291,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mDozeParameters = dozeParameters; mUpdateMonitor.registerCallback(this); mMediaManager = notificationMediaManager; + mLatencyTracker = latencyTracker; wakefulnessLifecycle.addObserver(mWakefulnessObserver); screenLifecycle.addObserver(mScreenObserver); @@ -343,13 +346,13 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp public void onBiometricAcquired(BiometricSourceType biometricSourceType) { Trace.beginSection("BiometricUnlockController#onBiometricAcquired"); releaseBiometricWakeLock(); - if (!mUpdateMonitor.isDeviceInteractive()) { - if (LatencyTracker.isEnabled(mContext)) { + if (isWakeAndUnlock()) { + if (mLatencyTracker.isEnabled()) { int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK; if (biometricSourceType == BiometricSourceType.FACE) { action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK; } - LatencyTracker.getInstance(mContext).onActionStart(action); + mLatencyTracker.onActionStart(action); } mWakeLock = mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME); @@ -652,6 +655,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType)) .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId())); + if (mLatencyTracker.isEnabled()) { + int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK; + if (biometricSourceType == BiometricSourceType.FACE) { + action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK; + } + mLatencyTracker.onActionCancel(action); + } + if (biometricSourceType == BiometricSourceType.FINGERPRINT && mUpdateMonitor.isUdfpsSupported()) { long currUptimeMillis = SystemClock.uptimeMillis(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 00b54e9e042a..2ec5f250eb48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard; @@ -27,7 +26,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.UserManager; import android.util.AttributeSet; import android.util.Pair; import android.util.TypedValue; @@ -47,7 +45,6 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.window.StatusBarWindowView; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -72,7 +69,6 @@ public class KeyguardStatusBarView extends RelativeLayout { private StatusIconContainer mStatusIconContainer; private boolean mKeyguardUserSwitcherEnabled; - private final UserManager mUserManager; private boolean mIsPrivacyDotEnabled; private int mSystemIconsSwitcherHiddenExpandedMargin; @@ -99,10 +95,10 @@ public class KeyguardStatusBarView extends RelativeLayout { */ private int mTopClipping; private final Rect mClipRect = new Rect(0, 0, 0, 0); + private boolean mIsUserSwitcherEnabled; public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); - mUserManager = UserManager.get(getContext()); } @Override @@ -163,6 +159,10 @@ public class KeyguardStatusBarView extends RelativeLayout { updateKeyguardStatusBarHeight(); } + public void setUserSwitcherEnabled(boolean enabled) { + mIsUserSwitcherEnabled = enabled; + } + private void updateKeyguardStatusBarHeight() { MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); lp.height = getStatusBarHeaderHeightKeyguard(mContext); @@ -200,11 +200,7 @@ public class KeyguardStatusBarView extends RelativeLayout { // If we have no keyguard switcher, the screen width is under 600dp. In this case, // we only show the multi-user switch if it's enabled through UserManager as well as // by the user. - // TODO(b/138661450) Move IPC calls to background - boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled( - mContext.getResources().getBoolean( - R.bool.qs_show_user_switcher_for_single_user))); - if (isMultiUserEnabled) { + if (mIsUserSwitcherEnabled) { mMultiUserAvatar.setVisibility(View.VISIBLE); } else { mMultiUserAvatar.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 81871634fbaf..ee97fd631818 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -23,8 +23,10 @@ import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedul import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.content.res.Configuration; import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; +import android.os.UserManager; import android.util.MathUtils; import android.view.View; @@ -92,6 +94,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final BiometricUnlockController mBiometricUnlockController; private final SysuiStatusBarStateController mStatusBarStateController; private final StatusBarContentInsetsProvider mInsetsProvider; + private final UserManager mUserManager; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -105,6 +108,11 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mView.onOverlayChanged(); KeyguardStatusBarViewController.this.onThemeChanged(); } + + @Override + public void onConfigChanged(Configuration newConfig) { + updateUserSwitcher(); + } }; private final SystemStatusAnimationCallback mAnimationCallback = @@ -159,6 +167,13 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } @Override + public void onKeyguardVisibilityChanged(boolean showing) { + if (showing) { + updateUserSwitcher(); + } + } + + @Override public void onBiometricRunningStateChanged( boolean running, BiometricSourceType biometricSourceType) { @@ -230,7 +245,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat KeyguardUpdateMonitor keyguardUpdateMonitor, BiometricUnlockController biometricUnlockController, SysuiStatusBarStateController statusBarStateController, - StatusBarContentInsetsProvider statusBarContentInsetsProvider + StatusBarContentInsetsProvider statusBarContentInsetsProvider, + UserManager userManager ) { super(view); mCarrierTextController = carrierTextController; @@ -248,6 +264,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mBiometricUnlockController = biometricUnlockController; mStatusBarStateController = statusBarStateController; mInsetsProvider = statusBarContentInsetsProvider; + mUserManager = userManager; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mKeyguardStateController.addCallback( @@ -293,7 +310,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } mView.setOnApplyWindowInsetsListener( (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider)); - + updateUserSwitcher(); onThemeChanged(); } @@ -437,6 +454,14 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } /** + * Updates visibility of the user switcher button based on {@link android.os.UserManager} state. + */ + private void updateUserSwitcher() { + mView.setUserSwitcherEnabled(mUserManager.isUserSwitcherEnabled(getResources().getBoolean( + R.bool.qs_show_user_switcher_for_single_user))); + } + + /** * Update {@link KeyguardStatusBarView}'s visibility based on whether keyguard is showing and * whether heads up is visible. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 1870588f34ae..3d3a1da1cf82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -20,6 +20,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.view.View.GONE; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM; import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; import static androidx.constraintlayout.widget.ConstraintSet.START; @@ -414,6 +415,7 @@ public class NotificationPanelViewController extends PanelViewController private int mDisplayTopInset = 0; // in pixels private int mDisplayRightInset = 0; // in pixels private int mSplitShadeStatusBarHeight; + private int mSplitShadeNotificationsScrimMarginBottom; private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm = @@ -1168,6 +1170,9 @@ public class NotificationPanelViewController extends PanelViewController public void updateResources() { mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext()); mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext()); + mSplitShadeNotificationsScrimMarginBottom = + mResources.getDimensionPixelSize( + R.dimen.split_shade_notifications_scrim_margin_bottom); int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); mShouldUseSplitNotificationShade = @@ -1177,6 +1182,8 @@ public class NotificationPanelViewController extends PanelViewController mQs.setInSplitShade(mShouldUseSplitNotificationShade); } + int notificationsBottomMargin = mResources.getDimensionPixelSize( + R.dimen.notification_panel_margin_bottom); int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight : mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade); @@ -1205,9 +1212,12 @@ public class NotificationPanelViewController extends PanelViewController constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin); + constraintSet.setMargin(R.id.notification_stack_scroller, BOTTOM, + notificationsBottomMargin); constraintSet.setMargin(R.id.qs_frame, TOP, topMargin); constraintSet.applyTo(mNotificationContainerParent); mAmbientState.setStackTopMargin(topMargin); + mNotificationsQSContainerController.updateMargins(); mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade); updateKeyguardStatusViewAlignment(/* animate= */false); @@ -2565,7 +2575,8 @@ public class NotificationPanelViewController extends PanelViewController right = getView().getRight() + mDisplayRightInset; } else { top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight); - bottom = top + mNotificationStackScrollLayoutController.getHeight(); + bottom = top + mNotificationStackScrollLayoutController.getHeight() + - mSplitShadeNotificationsScrimMarginBottom; left = mNotificationStackScrollLayoutController.getLeft(); right = mNotificationStackScrollLayoutController.getRight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt index 34bb6d3e1a27..1a885336be5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt @@ -63,7 +63,7 @@ class NotificationsQSContainerController @Inject constructor( } public override fun onViewAttached() { - notificationsBottomMargin = mView.defaultNotificationsMarginBottom + updateMargins() overviewProxyService.addCallback(taskbarVisibilityListener) mView.setInsetsChangedListener(windowInsetsListener) mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } @@ -75,6 +75,10 @@ class NotificationsQSContainerController @Inject constructor( mView.removeQSFragmentAttachedListener() } + fun updateMargins() { + notificationsBottomMargin = mView.defaultNotificationsMarginBottom + } + override fun setCustomizerAnimating(animating: Boolean) { if (isQSCustomizerAnimating != animating) { isQSCustomizerAnimating = animating diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index 9210a8b5db80..cecbcdb829c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -45,7 +45,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout private View mStackScroller; private View mKeyguardStatusBar; - private int mStackScrollerMargin; private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>(); private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>(); private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild); @@ -63,7 +62,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout super.onFinishInflate(); mQsFrame = findViewById(R.id.qs_frame); mStackScroller = findViewById(R.id.notification_stack_scroller); - mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin; mKeyguardStatusBar = findViewById(R.id.keyguard_header); } @@ -97,7 +95,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout } public int getDefaultNotificationsMarginBottom() { - return mStackScrollerMargin; + return ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin; } public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 1cb19ab727aa..d6bf5f21c834 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -49,29 +49,29 @@ class PhoneStatusBarViewController private constructor( } override fun onViewAttached() { - moveFromCenterAnimationController?.let { animationController -> - val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side) - val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area) - - val viewsToAnimate = arrayOf( - statusBarLeftSide, - systemIconArea - ) - - mView.viewTreeObserver.addOnPreDrawListener(object : - ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - animationController.onViewsReady(viewsToAnimate) - mView.viewTreeObserver.removeOnPreDrawListener(this) - return true - } - }) + if (moveFromCenterAnimationController == null) return + + val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side) + val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area) + + val viewsToAnimate = arrayOf( + statusBarLeftSide, + systemIconArea + ) + + mView.viewTreeObserver.addOnPreDrawListener(object : + ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + moveFromCenterAnimationController.onViewsReady(viewsToAnimate) + mView.viewTreeObserver.removeOnPreDrawListener(this) + return true + } + }) - mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ -> - val widthChanged = right - left != oldRight - oldLeft - if (widthChanged) { - moveFromCenterAnimationController.onStatusBarWidthChanged() - } + mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ -> + val widthChanged = right - left != oldRight - oldLeft + if (widthChanged) { + moveFromCenterAnimationController.onStatusBarWidthChanged() } } @@ -162,9 +162,7 @@ class PhoneStatusBarViewController private constructor( PhoneStatusBarViewController( view, progressProvider.getOrNull(), - unfoldComponent.map { - it.getStatusBarMoveFromCenterAnimationController() - }.getOrNull(), + unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(), touchEventHandler, configurationController ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 2f3300a53ad6..c8cc807475f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2935,14 +2935,6 @@ public class StatusBar extends CoreStartable implements return updateIsKeyguard(); } - /** - * stop(tag) - * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER. - */ - public boolean isFullScreenUserSwitcherState() { - return mState == StatusBarState.FULLSCREEN_USER_SWITCHER; - } - boolean updateIsKeyguard() { return updateIsKeyguard(false /* forceStateChange */); } @@ -2996,9 +2988,7 @@ public class StatusBar extends CoreStartable implements onLaunchTransitionFadingEnded(); } mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); - if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) { - mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER); - } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) { + if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) { mStatusBarStateController.setState(StatusBarState.KEYGUARD); } updatePanelExpansionForKeyguard(); @@ -3009,8 +2999,6 @@ public class StatusBar extends CoreStartable implements if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode() != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) { mShadeController.instantExpandNotificationsPanel(); - } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) { - instantCollapseNotificationPanel(); } } 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 b833c894c43f..d42a42364f21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -49,7 +49,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; -import com.android.systemui.DejankUtils; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; @@ -214,6 +213,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final SysuiStatusBarStateController mStatusBarStateController; private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; + private final LatencyTracker mLatencyTracker; private KeyguardBypassController mBypassController; @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; @@ -246,7 +246,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb NotificationMediaManager notificationMediaManager, KeyguardBouncer.Factory keyguardBouncerFactory, KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, - Lazy<ShadeController> shadeController) { + Lazy<ShadeController> shadeController, + LatencyTracker latencyTracker) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -262,6 +263,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardBouncerFactory = keyguardBouncerFactory; mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; mShadeController = shadeController; + mLatencyTracker = latencyTracker; } @Override @@ -859,15 +861,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private void wakeAndUnlockDejank() { - if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK - && LatencyTracker.isEnabled(mContext)) { + if (mBiometricUnlockController.isWakeAndUnlock() && mLatencyTracker.isEnabled()) { BiometricSourceType type = mBiometricUnlockController.getBiometricType(); - DejankUtils.postAfterTraversal(() -> { - LatencyTracker.getInstance(mContext).onActionEnd( - type == BiometricSourceType.FACE - ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK - : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK); - }); + mLatencyTracker.onActionEnd(type == BiometricSourceType.FACE + ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK + : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK); } } @@ -1186,7 +1184,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // When a dream overlay is active, scrimming will cause any expansion to immediately expand. return (mOccluded && !mDreamOverlayStateController.isOverlayActive()) || mBouncer.willDismissWithAction() - || mStatusBar.isFullScreenUserSwitcherState() || (mBouncer.isShowing() && mBouncer.isScrimmed()) || mBouncer.isFullscreenBouncer(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt index 6d033477e3c9..79c0984d9bf7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt @@ -18,11 +18,13 @@ package com.android.systemui.statusbar.phone import android.view.View import android.view.WindowManager import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator +import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.AlphaProvider import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider import com.android.systemui.unfold.SysUIUnfoldScope import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import javax.inject.Inject +import kotlin.math.max @SysUIUnfoldScope class StatusBarMoveFromCenterAnimationController @Inject constructor( @@ -31,8 +33,11 @@ class StatusBarMoveFromCenterAnimationController @Inject constructor( ) { private val transitionListener = TransitionListener() - private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager, - viewCenterProvider = StatusBarViewsCenterProvider()) + private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator( + windowManager, + viewCenterProvider = StatusBarViewsCenterProvider(), + alphaProvider = StatusBarIconsAlphaProvider() + ) fun onViewsReady(viewsToAnimate: Array<View>) { moveFromCenterAnimator.updateDisplayProperties() @@ -65,4 +70,15 @@ class StatusBarMoveFromCenterAnimationController @Inject constructor( moveFromCenterAnimator.onTransitionProgress(1f) } } + + private class StatusBarIconsAlphaProvider : AlphaProvider { + override fun getAlpha(progress: Float): Float { + return max( + 0f, + (progress - ICONS_START_APPEARING_PROGRESS) / (1 - ICONS_START_APPEARING_PROGRESS) + ) + } + } } + +private const val ICONS_START_APPEARING_PROGRESS = 0.75F diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 49e712d386e5..1b73595beb7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.policy; import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; -import static com.android.systemui.DejankUtils.whitelistIpcs; import android.annotation.UserIdInt; import android.app.ActivityManager; @@ -427,22 +426,6 @@ public class UserSwitcherController implements Dumpable { return mSimpleUserSwitcher; } - public boolean useFullscreenUserSwitcher() { - // Use adb to override: - // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off. - // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on. - // Restart SystemUI or adb reboot. - final int DEFAULT = -1; - final int overrideUseFullscreenUserSwitcher = - whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(), - "enable_fullscreen_user_switcher", DEFAULT)); - if (overrideUseFullscreenUserSwitcher != DEFAULT) { - return overrideUseFullscreenUserSwitcher != 0; - } - // Otherwise default to the build setting. - return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher); - } - public void setResumeUserOnGuestLogout(boolean resume) { mResumeUserOnGuestLogout = resume; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java new file mode 100644 index 000000000000..b02c506be8be --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.dreams.complication; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DreamClockDateComplicationTest extends SysuiTestCase { + @SuppressWarnings("HidingField") + @Mock + private Context mContext; + + @Mock + private DreamOverlayStateController mDreamOverlayStateController; + + @Mock + private DreamClockDateComplication mComplication; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Ensures {@link DreamClockDateComplication} is registered. + */ + @Test + public void testComplicationAdded() { + final DreamClockDateComplication.Registrant registrant = + new DreamClockDateComplication.Registrant( + mContext, + mDreamOverlayStateController, + mComplication); + registrant.start(); + verify(mDreamOverlayStateController).addComplication(eq(mComplication)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java new file mode 100644 index 000000000000..088b4d5136ff --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.dreams.complication; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DreamClockTimeComplicationTest extends SysuiTestCase { + @SuppressWarnings("HidingField") + @Mock + private Context mContext; + + @Mock + private DreamOverlayStateController mDreamOverlayStateController; + + @Mock + private DreamClockTimeComplication mComplication; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Ensures {@link DreamClockTimeComplication} is registered. + */ + @Test + public void testComplicationAdded() { + final DreamClockTimeComplication.Registrant registrant = + new DreamClockTimeComplication.Registrant( + mContext, + mDreamOverlayStateController, + mComplication); + registrant.start(); + verify(mDreamOverlayStateController).addComplication(eq(mComplication)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java new file mode 100644 index 000000000000..151742af7e1a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.dreams.complication; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DreamWeatherComplicationTest extends SysuiTestCase { + @SuppressWarnings("HidingField") + @Mock + private Context mContext; + + @Mock + private LockscreenSmartspaceController mSmartspaceController; + + @Mock + private DreamOverlayStateController mDreamOverlayStateController; + + @Mock + private DreamWeatherComplication mComplication; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Ensures {@link DreamWeatherComplication} is only registered when it is available. + */ + @Test + public void testComplicationAvailability() { + when(mSmartspaceController.isEnabled()).thenReturn(false); + final DreamWeatherComplication.Registrant registrant = + new DreamWeatherComplication.Registrant( + mContext, + mSmartspaceController, + mDreamOverlayStateController, + mComplication); + registrant.start(); + verify(mDreamOverlayStateController, never()).addComplication(any()); + + when(mSmartspaceController.isEnabled()).thenReturn(true); + registrant.start(); + verify(mDreamOverlayStateController).addComplication(eq(mComplication)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt index 43d9a755269f..dc7026da2194 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt @@ -95,7 +95,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() { fun testVisibleOnKeyguardOrFullScreenUserSwitcher() { testStateVisibility(StatusBarState.SHADE, GONE) testStateVisibility(StatusBarState.SHADE_LOCKED, GONE) - testStateVisibility(StatusBarState.FULLSCREEN_USER_SWITCHER, VISIBLE) testStateVisibility(StatusBarState.KEYGUARD, VISIBLE) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt new file mode 100644 index 000000000000..c26108619ff4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt @@ -0,0 +1,124 @@ +package com.android.systemui.media.nearby + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.media.INearbyMediaDevicesProvider +import com.android.systemui.shared.media.INearbyMediaDevicesService +import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback +import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_LONG +import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH +import com.android.systemui.shared.media.NearbyDevice +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test + +@SmallTest +class NearbyMediaDevicesServiceTest : SysuiTestCase() { + + private lateinit var service: NearbyMediaDevicesService + private lateinit var binderInterface: INearbyMediaDevicesService + + @Before + fun setUp() { + service = NearbyMediaDevicesService() + binderInterface = INearbyMediaDevicesService.Stub.asInterface(service.onBind(null)) + } + + @Test + fun getCurrentNearbyDevices_noProviderRegistered_returnsEmptyList() { + assertThat(service.getCurrentNearbyDevices()).isEmpty() + } + + @Test + fun getCurrentNearbyDevices_providerRegistered_returnsProviderInfo() { + val nearbyDevice1 = NearbyDevice("routeId1", RANGE_LONG) + val nearbyDevice2 = NearbyDevice("routeId2", RANGE_WITHIN_REACH) + val provider = object : INearbyMediaDevicesProvider.Stub() { + override fun getCurrentNearbyDevices(): List<NearbyDevice> { + return listOf(nearbyDevice1, nearbyDevice2) + } + + override fun registerNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) {} + override fun unregisterNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) {} + } + binderInterface.registerProvider(provider) + + val returnedNearbyDevices = service.getCurrentNearbyDevices() + + assertThat(returnedNearbyDevices).isEqualTo(listOf(nearbyDevice1, nearbyDevice2)) + } + + @Test + fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() { + // No assert, just needs no crash + service.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() { + override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {} + }) + } + + @Test + fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() { + val provider = object : INearbyMediaDevicesProvider.Stub() { + var registeredCallback: INearbyMediaDevicesUpdateCallback? = null + override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf() + + override fun registerNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) { + registeredCallback = callback + } + + override fun unregisterNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) {} + } + binderInterface.registerProvider(provider) + + val callback = object : INearbyMediaDevicesUpdateCallback.Stub() { + override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {} + } + + service.registerNearbyDevicesCallback(callback) + + assertThat(provider.registeredCallback).isEqualTo(callback) + } + + @Test + fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() { + // No assert, just needs no crash + service.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() { + override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {} + }) + } + + @Test + fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() { + val provider = object : INearbyMediaDevicesProvider.Stub() { + var unregisteredCallback: INearbyMediaDevicesUpdateCallback? = null + override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf() + + override fun registerNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) {} + + override fun unregisterNearbyDevicesCallback( + callback: INearbyMediaDevicesUpdateCallback? + ) { + unregisteredCallback = callback + } + } + binderInterface.registerProvider(provider) + + val callback = object : INearbyMediaDevicesUpdateCallback.Stub() { + override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {} + } + + service.unregisterNearbyDevicesCallback(callback) + + assertThat(provider.unregisteredCallback).isEqualTo(callback) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index d51d370eecb9..9076e1607be5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -169,8 +169,6 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.goToLockedShade(null) whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE) transitionController.goToLockedShade(null) - whenever(statusbarStateController.state).thenReturn(StatusBarState.FULLSCREEN_USER_SWITCHER) - transitionController.goToLockedShade(null) verify(statusbarStateController, never()).setState(anyInt()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index b736f389ad6c..a5ea897b6a6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -61,18 +61,16 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { @Test fun testChangeState_logged() { TestableLooper.get(this).runWithLooper { - controller.state = StatusBarState.FULLSCREEN_USER_SWITCHER controller.state = StatusBarState.KEYGUARD controller.state = StatusBarState.SHADE controller.state = StatusBarState.SHADE_LOCKED } val logs = uiEventLogger.logs - assertEquals(4, logs.size) + assertEquals(3, logs.size) val ids = logs.map(UiEventLoggerFake.FakeUiEvent::eventId) - assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER.id, ids[0]) - assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[1]) - assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[2]) - assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[3]) + assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[0]) + assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[1]) + assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[2]) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt index b5b2f1fc0484..79a2008e7542 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt @@ -33,18 +33,16 @@ class StatusBarStateEventTest : SysuiTestCase() { StatusBarStateEvent.STATUS_BAR_STATE_SHADE, StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED, StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD, - StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER, StatusBarStateEvent.STATUS_BAR_STATE_UNKNOWN ) val states = listOf( StatusBarState.SHADE, StatusBarState.SHADE_LOCKED, StatusBarState.KEYGUARD, - StatusBarState.FULLSCREEN_USER_SWITCHER, -1 ) events.zip(states).forEach { (event, state) -> assertEquals(event, StatusBarStateEvent.fromState(state)) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index ea681435132e..1da9bbcdb836 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -69,10 +69,9 @@ class StackScrollAlgorithmTest : SysuiTestCase() { stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) - val closeHandleUnderlapHeight = - context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap) - val fullHeight = - ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY + val marginBottom = + context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom) + val fullHeight = ambientState.layoutMaxHeight + marginBottom - ambientState.stackY val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 8c7d22dde0b7..fb232ba3ac2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -38,6 +38,7 @@ import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableResources; import com.android.internal.logging.MetricsLogger; +import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; @@ -110,6 +111,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private SessionTracker mSessionTracker; + @Mock + private LatencyTracker mLatencyTracker; private BiometricUnlockController mBiometricUnlockController; @Before @@ -133,7 +136,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mMetricsLogger, mDumpManager, mPowerManager, mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle, mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController, - mSessionTracker); + mSessionTracker, mLatencyTracker); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 36a4c1e5ebfc..01e9822e0484 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -23,9 +23,13 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.UserManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.LayoutInflater; @@ -45,6 +49,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoController; @@ -52,6 +57,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -87,6 +93,12 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { private SysuiStatusBarStateController mStatusBarStateController; @Mock private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider; + @Mock + private UserManager mUserManager; + @Captor + private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor; + @Captor + private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor; private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider; private KeyguardStatusBarView mKeyguardStatusBarView; @@ -101,8 +113,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { allowTestableLooperAsMainThread(); TestableLooper.get(this).runWithLooper(() -> { mKeyguardStatusBarView = - (KeyguardStatusBarView) LayoutInflater.from(mContext) - .inflate(R.layout.keyguard_status_bar, null); + spy((KeyguardStatusBarView) LayoutInflater.from(mContext) + .inflate(R.layout.keyguard_status_bar, null)); }); mController = new KeyguardStatusBarViewController( @@ -121,7 +133,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor, mBiometricUnlockController, mStatusBarStateController, - mStatusBarContentInsetsProvider + mStatusBarContentInsetsProvider, + mUserManager ); } @@ -133,6 +146,31 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { verify(mAnimationScheduler).addCallback(any()); verify(mUserInfoController).addCallback(any()); verify(mStatusBarIconController).addIconGroup(any()); + verify(mUserManager).isUserSwitcherEnabled(anyBoolean()); + } + + @Test + public void onConfigurationChanged_updatesUserSwitcherVisibility() { + mController.onViewAttached(); + verify(mConfigurationController).addCallback(mConfigurationListenerCaptor.capture()); + clearInvocations(mUserManager); + clearInvocations(mKeyguardStatusBarView); + + mConfigurationListenerCaptor.getValue().onConfigChanged(null); + verify(mUserManager).isUserSwitcherEnabled(anyBoolean()); + verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean()); + } + + @Test + public void onKeyguardVisibilityChanged_updatesUserSwitcherVisibility() { + mController.onViewAttached(); + verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture()); + clearInvocations(mUserManager); + clearInvocations(mKeyguardStatusBarView); + + mKeyguardCallbackCaptor.getValue().onKeyguardVisibilityChanged(true); + verify(mUserManager).isUserSwitcherEnabled(anyBoolean()); + verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt index 337e64592750..77e90256ab70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt @@ -215,6 +215,18 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { then(expectedContainerPadding = 0) } + @Test + fun testNotificationsMarginBottomIsUpdated() { + notificationsQSContainerController.splitShadeEnabled = true + verify(notificationsQSContainer).setNotificationsMarginBottom(NOTIFICATIONS_MARGIN) + + whenever(notificationsQSContainer.defaultNotificationsMarginBottom).thenReturn(100) + notificationsQSContainerController.updateMargins() + notificationsQSContainerController.splitShadeEnabled = false + + verify(notificationsQSContainer).setNotificationsMarginBottom(100) + } + private fun given( taskbarVisible: Boolean, navigationMode: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index bb79941b0e53..107ba8130349 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; +import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; @@ -100,6 +101,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private ShadeController mShadeController; @Mock private DreamOverlayStateController mDreamOverlayStateController; + @Mock + private LatencyTracker mLatencyTracker; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -127,7 +130,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mock(NotificationMediaManager.class), mKeyguardBouncerFactory, mKeyguardMessageAreaFactory, - () -> mShadeController); + () -> mShadeController, + mLatencyTracker); mStatusBarKeyguardViewManager.registerStatusBar( mStatusBar, mNotificationPanelView, @@ -171,17 +175,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void onPanelExpansionChanged_neverHidesFullscreenBouncer() { - // TODO: StatusBar should not be here, mBouncer.isFullscreenBouncer() should do the same. - when(mStatusBar.isFullScreenUserSwitcherState()).thenReturn(true); - mStatusBarKeyguardViewManager.onPanelExpansionChanged( - /* fraction= */ 0.5f, - /* expanded= */ false, - /* tracking= */ true); - verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE)); - } - - @Test public void onPanelExpansionChanged_neverHidesScrimmedBouncer() { when(mBouncer.isShowing()).thenReturn(true); when(mBouncer.isScrimmed()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 1564dfe8cd06..90b93e797410 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -885,15 +885,6 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testSetState_changesIsFullScreenUserSwitcherState() { - mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); - assertFalse(mStatusBar.isFullScreenUserSwitcherState()); - - mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER); - assertTrue(mStatusBar.isFullScreenUserSwitcherState()); - } - - @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); @@ -903,12 +894,6 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.showKeyguardImpl(); verify(mStatusBarStateController).setState( eq(StatusBarState.KEYGUARD), eq(false) /* force */); - - // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER. - when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true); - mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState( - eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */); } @Test diff --git a/services/Android.bp b/services/Android.bp index af70692a88e5..b0a5c66b53dd 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -102,6 +102,7 @@ filegroup { ":services.usage-sources", ":services.usb-sources", ":services.voiceinteraction-sources", + ":services.wallpapereffectsgeneration-sources", ":services.wifi-sources", ], visibility: ["//visibility:private"], @@ -158,6 +159,7 @@ java_library { "services.usage", "services.usb", "services.voiceinteraction", + "services.wallpapereffectsgeneration", "services.wifi", "service-blobstore", "service-jobscheduler", diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index 6744ea8e26a5..803177b6c010 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -295,6 +295,21 @@ public class SystemActionPerformer { case AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK : sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK); return true; + case AccessibilityService.GLOBAL_ACTION_DPAD_UP: + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP); + return true; + case AccessibilityService.GLOBAL_ACTION_DPAD_DOWN: + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN); + return true; + case AccessibilityService.GLOBAL_ACTION_DPAD_LEFT: + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); + return true; + case AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT: + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT); + return true; + case AccessibilityService.GLOBAL_ACTION_DPAD_CENTER: + sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER); + return true; default: Slog.e(TAG, "Invalid action id: " + actionId); return false; diff --git a/services/core/Android.bp b/services/core/Android.bp index 1106fe7270e6..561009fe2a71 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -134,6 +134,7 @@ java_library_static { "app-compat-annotations", "framework-tethering.stubs.module_lib", "service-permission.stubs.system_server", + "service-supplementalprocess.stubs.system_server", ], required: [ diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index 435d294a3e8e..a35aa7c74ee5 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager.ProcessState; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.content.ComponentName; import android.content.LocusId; @@ -375,10 +376,11 @@ public abstract class UsageStatsManagerInternal { * to this broadcast. * @param timestampMs time (in millis) when the broadcast was dispatched, in * {@link SystemClock#elapsedRealtime()} timebase. + * @param targetUidProcState process state of the uid that the broadcast is targeted to. */ public abstract void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage, @NonNull UserHandle targetUser, long idForResponseEvent, - @ElapsedRealtimeLong long timestampMs); + @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState); /** * Reports a notification posted event to the UsageStatsManager. diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 0bc3fcc83d47..ce30f0348f11 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -576,19 +576,20 @@ public final class SystemServiceManager implements Dumpable { return () -> { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(oldTrace); final String serviceName = service.getClass().getName(); + final int curUserId = curUser.getUserIdentifier(); + t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName); try { - final int curUserId = curUser.getUserIdentifier(); - t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName); long time = SystemClock.elapsedRealtime(); service.onUserStarting(curUser); warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + USER_STARTING + "User-" + curUserId); - t.traceEnd(); } catch (Exception e) { Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser + " to service " + serviceName, e); Slog.e(TAG, "Disabling thread pool - please capture a bug report."); sUseLifecycleThreadPool = false; + } finally { + t.traceEnd(); } }; } @@ -601,11 +602,18 @@ public final class SystemServiceManager implements Dumpable { final int curUserId = curUser.getUserIdentifier(); t.traceBegin("ssm.on" + USER_COMPLETED_EVENT + "User-" + curUserId + "_" + eventType + "_" + serviceName); - long time = SystemClock.elapsedRealtime(); - service.onUserCompletedEvent(curUser, eventType); - warnIfTooLong(SystemClock.elapsedRealtime() - time, service, - "on" + USER_COMPLETED_EVENT + "User-" + curUserId); - t.traceEnd(); + try { + long time = SystemClock.elapsedRealtime(); + service.onUserCompletedEvent(curUser, eventType); + warnIfTooLong(SystemClock.elapsedRealtime() - time, service, + "on" + USER_COMPLETED_EVENT + "User-" + curUserId); + } catch (Exception e) { + Slog.wtf(TAG, "Failure reporting " + USER_COMPLETED_EVENT + " of user " + curUser + + " to service " + serviceName, e); + throw e; + } finally { + t.traceEnd(); + } }; } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 0c383ebbbcd7..e2921e9f5e71 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -334,7 +334,7 @@ public final class BroadcastQueue { mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); // Tell the application to launch this receiver. - maybeReportBroadcastDispatchedEventLocked(r); + maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid); r.intent.setComponent(r.curComponent); boolean started = false; @@ -927,7 +927,7 @@ public final class BroadcastQueue { r.receiverTime = SystemClock.uptimeMillis(); maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r); maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options); - maybeReportBroadcastDispatchedEventLocked(r); + maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid); performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId); @@ -1856,7 +1856,7 @@ public final class BroadcastQueue { return null; } - private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r) { + private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) { final String targetPackage = getTargetPackage(r); // Ignore non-explicit broadcasts if (targetPackage == null) { @@ -1867,11 +1867,10 @@ public final class BroadcastQueue { if (r.options == null || r.options.getIdForResponseEvent() <= 0) { return; } - // TODO (206518114): Only report this event when the broadcast is dispatched while the app - // is in the background state. getUsageStatsManagerInternal().reportBroadcastDispatched( r.callingUid, targetPackage, UserHandle.of(r.userId), - r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime()); + r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(), + mService.getUidStateLocked(targetUid)); } @NonNull diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 3c9d29d77bbe..551773e3e834 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -20,6 +20,11 @@ import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_PACKAGE_CHANGED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver; +import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling; +import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride; +import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode; +import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode; import static com.android.server.wm.CompatModePackages.DOWNSCALED; import static com.android.server.wm.CompatModePackages.DOWNSCALE_30; import static com.android.server.wm.CompatModePackages.DOWNSCALE_35; @@ -54,6 +59,10 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.hardware.power.Mode; import android.net.Uri; import android.os.Binder; @@ -71,8 +80,10 @@ import android.os.ShellCallback; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.util.ArrayMap; +import android.util.AttributeSet; import android.util.KeyValueListParser; import android.util.Slog; +import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -84,7 +95,11 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.List; @@ -425,12 +440,20 @@ public final class GameManagerService extends IGameManagerService.Stub { public static final String METADATA_BATTERY_MODE_ENABLE = "com.android.app.gamemode.battery.enabled"; + /** + * Metadata that allows a game to specify all intervention information with an XML file in + * the application field. + */ + public static final String METADATA_GAME_MODE_CONFIG = "android.game_mode_config"; + + private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config"; private final String mPackageName; private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs; private boolean mPerfModeOptedIn; private boolean mBatteryModeOptedIn; private boolean mAllowDownscale; private boolean mAllowAngle; + private boolean mAllowFpsOverride; GamePackageConfiguration(String packageName, int userId) { mPackageName = packageName; @@ -438,18 +461,21 @@ public final class GameManagerService extends IGameManagerService.Stub { try { final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName, PackageManager.GET_META_DATA, userId); - if (ai.metaData != null) { - mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE); - mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE); - mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true); - mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true); - } else { - mPerfModeOptedIn = false; - mBatteryModeOptedIn = false; - mAllowDownscale = true; - mAllowAngle = true; + if (!parseInterventionFromXml(ai, packageName)) { + if (ai.metaData != null) { + mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE); + mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE); + mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true); + mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true); + } else { + mPerfModeOptedIn = false; + mBatteryModeOptedIn = false; + mAllowDownscale = true; + mAllowAngle = true; + mAllowFpsOverride = true; + } } - } catch (PackageManager.NameNotFoundException e) { + } catch (NameNotFoundException e) { // Not all packages are installed, hence ignore those that are not installed yet. Slog.v(TAG, "Failed to get package metadata"); } @@ -469,6 +495,53 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + private boolean parseInterventionFromXml(ApplicationInfo ai, String packageName) { + boolean xmlFound = false; + try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager, + METADATA_GAME_MODE_CONFIG)) { + if (parser == null) { + Slog.v(TAG, "No " + METADATA_GAME_MODE_CONFIG + + " meta-data found for package " + mPackageName); + } else { + xmlFound = true; + final Resources resources = mPackageManager.getResourcesForApplication( + packageName); + final AttributeSet attributeSet = Xml.asAttributeSet(parser); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // Do nothing + } + + boolean isStartingTagGameModeConfig = + GAME_MODE_CONFIG_NODE_NAME.equals(parser.getName()); + if (!isStartingTagGameModeConfig) { + Slog.w(TAG, "Meta-data does not start with " + + GAME_MODE_CONFIG_NODE_NAME + + " tag"); + } else { + final TypedArray array = resources.obtainAttributes(attributeSet, + com.android.internal.R.styleable.GameModeConfig); + mPerfModeOptedIn = array.getBoolean( + GameModeConfig_supportsPerformanceGameMode, false); + mBatteryModeOptedIn = array.getBoolean( + GameModeConfig_supportsBatteryGameMode, + false); + mAllowDownscale = array.getBoolean(GameModeConfig_allowGameDownscaling, + true); + mAllowAngle = array.getBoolean(GameModeConfig_allowGameAngleDriver, true); + mAllowFpsOverride = array.getBoolean(GameModeConfig_allowGameFpsOverride, + true); + array.recycle(); + } + } + } catch (NameNotFoundException | XmlPullParserException | IOException ex) { + Slog.e(TAG, "Error while parsing XML meta-data for " + + METADATA_GAME_MODE_CONFIG); + } + return xmlFound; + } + /** * GameModeConfiguration contains all the values for all the interventions associated with * a game mode. @@ -497,7 +570,8 @@ public final class GameManagerService extends IGameManagerService.Stub { mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode) ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING); - mFps = parser.getString(FPS_KEY, DEFAULT_FPS); + mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode) + ? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS; // We only want to use ANGLE if: // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND // - The app has not opted in to performing the work itself AND @@ -691,7 +765,7 @@ public final class GameManagerService extends IGameManagerService.Stub { try { return mPackageManager.getPackageUidAsUser(packageName, userId) == Binder.getCallingUid(); - } catch (PackageManager.NameNotFoundException e) { + } catch (NameNotFoundException e) { return false; } } @@ -855,7 +929,7 @@ public final class GameManagerService extends IGameManagerService.Stub { */ @Override @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) - public @GameMode boolean getAngleEnabled(String packageName, int userId) + public @GameMode boolean isAngleEnabled(String packageName, int userId) throws SecurityException { final int gameMode = getGameMode(packageName, userId); if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) { @@ -1413,7 +1487,7 @@ public final class GameManagerService extends IGameManagerService.Stub { if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) { return; } - } catch (PackageManager.NameNotFoundException e) { + } catch (NameNotFoundException e) { // Ignore the exception. } switch (intent.getAction()) { diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index af1dd335ab1e..960fbf1d9330 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.server.wm.WindowManagerInternal; +import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerService; import java.util.List; @@ -62,6 +63,18 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan private static final int CREATE_GAME_SESSION_TIMEOUT_MS = 10_000; private static final boolean DEBUG = false; + private final TaskSystemBarsListener mTaskSystemBarsVisibilityListener = + new TaskSystemBarsListener() { + @Override + public void onTransientSystemBarsVisibilityChanged( + int taskId, + boolean visible, + boolean wereRevealedFromSwipeOnSystemBar) { + GameServiceProviderInstanceImpl.this.onTransientSystemBarsVisibilityChanged( + taskId, visible, wereRevealedFromSwipeOnSystemBar); + } + }; + private final TaskStackListener mTaskStackListener = new TaskStackListener() { @Override public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException { @@ -203,6 +216,8 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } catch (RemoteException e) { Slog.w(TAG, "Failed to register task stack listener", e); } + + mWindowManagerInternal.registerTaskSystemBarsListener(mTaskSystemBarsVisibilityListener); } @GuardedBy("mLock") @@ -218,6 +233,9 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan Slog.w(TAG, "Failed to unregister task stack listener", e); } + mWindowManagerInternal.unregisterTaskSystemBarsListener( + mTaskSystemBarsVisibilityListener); + for (GameSessionRecord gameSessionRecord : mGameSessions.values()) { destroyGameSessionFromRecord(gameSessionRecord); } @@ -307,6 +325,37 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } } + private void onTransientSystemBarsVisibilityChanged( + int taskId, + boolean visible, + boolean wereRevealedFromSwipeOnSystemBar) { + if (visible && !wereRevealedFromSwipeOnSystemBar) { + return; + } + + GameSessionRecord gameSessionRecord; + synchronized (mLock) { + gameSessionRecord = mGameSessions.get(taskId); + } + + if (gameSessionRecord == null) { + return; + } + + IGameSession gameSession = gameSessionRecord.getGameSession(); + if (gameSession == null) { + return; + } + + try { + gameSession.onTransientSystemBarVisibilityFromRevealGestureChanged(visible); + } catch (RemoteException ex) { + Slog.w(TAG, + "Failed to send transient system bars visibility from reveal gesture for task: " + + taskId); + } + } + private void createGameSession(int taskId) { synchronized (mLock) { createGameSessionLocked(taskId); diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index a1395890fd3c..d2fa386ad6ba 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -22,6 +22,7 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES; import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED; import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN; +import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN; import android.Manifest; import android.annotation.NonNull; @@ -29,6 +30,7 @@ import android.annotation.Nullable; import android.app.ActivityThread; import android.attention.AttentionManagerInternal; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.attention.AttentionManagerInternal.ProximityCallbackInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -57,6 +59,7 @@ import android.service.attention.AttentionService.AttentionFailureCodes; import android.service.attention.AttentionService.AttentionSuccessCodes; import android.service.attention.IAttentionCallback; import android.service.attention.IAttentionService; +import android.service.attention.IProximityCallback; import android.text.TextUtils; import android.util.Slog; @@ -134,6 +137,15 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; + /** + * A proxy for relaying proximity information between the Attention Service and the client. + * The proxy will be initialized when the client calls onStartProximityUpdates and will be + * disabled only when the client calls onStopProximityUpdates. + */ + @VisibleForTesting + @GuardedBy("mLock") + ProximityUpdate mCurrentProximityUpdate; + public AttentionManagerService(Context context) { this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), new Object(), null); @@ -315,6 +327,77 @@ public class AttentionManagerService extends SystemService { } } + /** + * Requests the continuous updates of proximity signal via the provided callback, + * until the given callback is stopped. + * + * Calling this multiple times for duplicate requests will be no-ops, returning true. + * + * @return {@code true} if the framework was able to dispatch the request + */ + @VisibleForTesting + boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) { + Objects.requireNonNull(callbackInternal); + if (!mIsServiceEnabled) { + Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device."); + return false; + } + + if (!isServiceAvailable()) { + Slog.w(LOG_TAG, "Service is not available at this moment."); + return false; + } + + // don't allow proximity request in screen off state. + // This behavior might change in the future. + if (!mPowerManager.isInteractive()) { + Slog.w(LOG_TAG, "Proximity Service is unavailable during screen off at this moment."); + return false; + } + + synchronized (mLock) { + // schedule shutting down the connection if no one resets this timer + freeIfInactiveLocked(); + + // lazily start the service, which should be very lightweight to start + bindLocked(); + + /* + Prevent spamming with multiple requests, only one at a time is allowed. + If there are use-cases for keeping track of multiple requests, we + can refactor ProximityUpdate object to keep track of multiple internal callbacks. + */ + if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) { + if (mCurrentProximityUpdate.mCallbackInternal == callbackInternal) { + Slog.w(LOG_TAG, "Provided callback is already registered. Skipping."); + return true; + } else { + // reject the new request since the old request is still alive. + Slog.w(LOG_TAG, "New proximity update cannot be processed because there is " + + "already an ongoing update"); + return false; + } + } + mCurrentProximityUpdate = new ProximityUpdate(callbackInternal); + return mCurrentProximityUpdate.startUpdates(); + } + } + + /** Cancels the specified proximity registration. */ + @VisibleForTesting + void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) { + synchronized (mLock) { + if (mCurrentProximityUpdate == null + || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal) + || !mCurrentProximityUpdate.mStartedUpdates) { + Slog.w(LOG_TAG, "Cannot stop a non-current callback"); + return; + } + mCurrentProximityUpdate.cancelUpdates(); + mCurrentProximityUpdate = null; + } + } + @GuardedBy("mLock") @VisibleForTesting protected void freeIfInactiveLocked() { @@ -390,15 +473,18 @@ public class AttentionManagerService extends SystemService { ipw.println("Class=" + mComponentName.getClassName()); ipw.decreaseIndent(); } - ipw.println("binding=" + mBinding); - ipw.println("current attention check:"); synchronized (mLock) { + ipw.println("binding=" + mBinding); + ipw.println("current attention check:"); if (mCurrentAttentionCheck != null) { mCurrentAttentionCheck.dump(ipw); } if (mAttentionCheckCacheBuffer != null) { mAttentionCheckCacheBuffer.dump(ipw); } + if (mCurrentProximityUpdate != null) { + mCurrentProximityUpdate.dump(ipw); + } } } @@ -417,6 +503,17 @@ public class AttentionManagerService extends SystemService { public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { AttentionManagerService.this.cancelAttentionCheck(callbackInternal); } + + @Override + public boolean onStartProximityUpdates( + ProximityCallbackInternal callback) { + return AttentionManagerService.this.onStartProximityUpdates(callback); + } + + @Override + public void onStopProximityUpdates(ProximityCallbackInternal callback) { + AttentionManagerService.this.onStopProximityUpdates(callback); + } } @VisibleForTesting @@ -536,6 +633,71 @@ public class AttentionManagerService extends SystemService { } } + @VisibleForTesting + final class ProximityUpdate { + private final ProximityCallbackInternal mCallbackInternal; + private final IProximityCallback mIProximityCallback; + private boolean mStartedUpdates; + + ProximityUpdate(ProximityCallbackInternal callbackInternal) { + mCallbackInternal = callbackInternal; + mIProximityCallback = new IProximityCallback.Stub() { + @Override + public void onProximityUpdate(double distance) { + synchronized (mLock) { + mCallbackInternal.onProximityUpdate(distance); + freeIfInactiveLocked(); + } + } + }; + } + + boolean startUpdates() { + synchronized (mLock) { + if (mStartedUpdates) { + Slog.w(LOG_TAG, "Already registered to a proximity service."); + return false; + } + if (mService == null) { + Slog.w(LOG_TAG, + "There is no service bound. Proximity update request rejected."); + return false; + } + try { + mService.onStartProximityUpdates(mIProximityCallback); + mStartedUpdates = true; + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); + return false; + } + } + return true; + } + + void cancelUpdates() { + synchronized (mLock) { + if (mStartedUpdates) { + if (mService == null) { + mStartedUpdates = false; + return; + } + try { + mService.onStopProximityUpdates(); + mStartedUpdates = false; + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); + } + } + } + } + + void dump(IndentingPrintWriter ipw) { + ipw.increaseIndent(); + ipw.println("is StartedUpdates=" + mStartedUpdates); + ipw.decreaseIndent(); + } + } + private void appendResultToAttentionCacheBuffer(AttentionCheckCache cache) { synchronized (mLock) { if (mAttentionCheckCacheBuffer == null) { @@ -593,6 +755,18 @@ public class AttentionManagerService extends SystemService { mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); } } + if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) { + if (mService != null) { + try { + mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); + } + } else { + mCurrentProximityUpdate.cancelUpdates(); + mCurrentProximityUpdate = null; + } + } } @VisibleForTesting @@ -609,7 +783,9 @@ public class AttentionManagerService extends SystemService { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. case CHECK_CONNECTION_EXPIRATION: { - cancelAndUnbindLocked(); + synchronized (mLock) { + cancelAndUnbindLocked(); + } } break; @@ -653,10 +829,15 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") private void cancelAndUnbindLocked() { synchronized (mLock) { - if (mCurrentAttentionCheck == null) { + if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) { return; } - cancel(); + if (mCurrentAttentionCheck != null) { + cancel(); + } + if (mCurrentProximityUpdate != null) { + mCurrentProximityUpdate.cancelUpdates(); + } if (mService == null) { return; } @@ -702,7 +883,9 @@ public class AttentionManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - cancelAndUnbindLocked(); + synchronized (mLock) { + cancelAndUnbindLocked(); + } } } } @@ -730,8 +913,27 @@ public class AttentionManagerService extends SystemService { } } + class TestableProximityCallbackInternal extends ProximityCallbackInternal { + private double mLastCallbackCode = PROXIMITY_UNKNOWN; + + @Override + public void onProximityUpdate(double distance) { + mLastCallbackCode = distance; + } + + public void reset() { + mLastCallbackCode = PROXIMITY_UNKNOWN; + } + + public double getLastCallbackCode() { + return mLastCallbackCode; + } + } + final TestableAttentionCallbackInternal mTestableAttentionCallback = new TestableAttentionCallbackInternal(); + final TestableProximityCallbackInternal mTestableProximityCallback = + new TestableProximityCallbackInternal(); @Override public int onCommand(@Nullable final String cmd) { @@ -749,6 +951,10 @@ public class AttentionManagerService extends SystemService { return cmdCallCheckAttention(); case "cancelCheckAttention": return cmdCallCancelAttention(); + case "onStartProximityUpdates": + return cmdCallOnStartProximityUpdates(); + case "onStopProximityUpdates": + return cmdCallOnStopProximityUpdates(); default: throw new IllegalArgumentException("Invalid argument"); } @@ -758,6 +964,8 @@ public class AttentionManagerService extends SystemService { return cmdClearTestableAttentionService(); case "getLastTestCallbackCode": return cmdGetLastTestCallbackCode(); + case "getLastTestProximityCallbackCode": + return cmdGetLastTestProximityCallbackCode(); default: return handleDefaultCommands(cmd); } @@ -782,6 +990,7 @@ public class AttentionManagerService extends SystemService { private int cmdClearTestableAttentionService() { sTestAttentionServicePackage = ""; mTestableAttentionCallback.reset(); + mTestableProximityCallback.reset(); resetStates(); return 0; } @@ -800,6 +1009,20 @@ public class AttentionManagerService extends SystemService { return 0; } + private int cmdCallOnStartProximityUpdates() { + final PrintWriter out = getOutPrintWriter(); + boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback); + out.println(calledSuccessfully ? "true" : "false"); + return 0; + } + + private int cmdCallOnStopProximityUpdates() { + final PrintWriter out = getOutPrintWriter(); + onStopProximityUpdates(mTestableProximityCallback); + out.println("true"); + return 0; + } + private int cmdResolveAttentionServiceComponent() { final PrintWriter out = getOutPrintWriter(); ComponentName resolvedComponent = resolveAttentionService(mContext); @@ -813,7 +1036,16 @@ public class AttentionManagerService extends SystemService { return 0; } + private int cmdGetLastTestProximityCallbackCode() { + final PrintWriter out = getOutPrintWriter(); + out.println(mTestableProximityCallback.getLastCallbackCode()); + return 0; + } + private void resetStates() { + synchronized (mLock) { + mCurrentProximityUpdate = null; + } mComponentName = resolveAttentionService(mContext); } @@ -844,11 +1076,24 @@ public class AttentionManagerService extends SystemService { + " (to see the result, call getLastTestCallbackCode)"); out.println(" := false, otherwise"); out.println(" call cancelCheckAttention: Cancels check attention"); + out.println(" call onStartProximityUpdates: Calls onStartProximityUpdates"); + out.println(" ---returns:"); + out.println( + " := true, if the request was successfully dispatched to the service " + + "implementation." + + " (to see the result, call getLastTestProximityCallbackCode)"); + out.println(" := false, otherwise"); + out.println(" call onStopProximityUpdates: Cancels proximity updates"); out.println(" getLastTestCallbackCode"); out.println(" ---returns:"); out.println( " := An integer, representing the last callback code received from the " + "bounded implementation. If none, it will return -1"); + out.println(" getLastTestProximityCallbackCode"); + out.println(" ---returns:"); + out.println( + " := A double, representing the last proximity value received from the " + + "bounded implementation. If none, it will return -1.0"); } } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 28508f46bfe6..53fe4509b1cc 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -1090,6 +1090,10 @@ public class ClipboardService extends SystemService { && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) { return; } + if (mPm.checkPermission(Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION, + callingPackage) == PackageManager.PERMISSION_GRANTED) { + return; + } // Don't notify if already notified for this uid and clip. if (clipboard.mNotifiedUids.get(uid)) { return; diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 1b552577308d..162eb0e188b9 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -201,6 +201,10 @@ class AutomaticBrightnessController { // Controls High Brightness Mode. private HighBrightnessModeController mHbmController; + // Throttles (caps) maximum allowed brightness + private BrightnessThrottler mBrightnessThrottler; + private boolean mIsBrightnessThrottled; + // Context-sensitive brightness configurations require keeping track of the foreground app's // package name and category, which is done by registering a TaskStackListener to call back to // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's @@ -226,7 +230,7 @@ class AutomaticBrightnessController { long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, Context context, - HighBrightnessModeController hbmController, + HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, @@ -235,8 +239,8 @@ class AutomaticBrightnessController { lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, context, - hbmController, idleModeBrightnessMapper, ambientLightHorizonShort, - ambientLightHorizonLong + hbmController, brightnessThrottler, idleModeBrightnessMapper, + ambientLightHorizonShort, ambientLightHorizonLong ); } @@ -249,7 +253,7 @@ class AutomaticBrightnessController { long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, Context context, - HighBrightnessModeController hbmController, + HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong) { mInjector = injector; @@ -291,6 +295,7 @@ class AutomaticBrightnessController { mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mHbmController = hbmController; + mBrightnessThrottler = brightnessThrottler; mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper; mIdleModeBrightnessMapper = idleModeBrightnessMapper; // Initialize to active (normal) screen brightness mode @@ -365,6 +370,13 @@ class AutomaticBrightnessController { prepareBrightnessAdjustmentSample(); } changed |= setLightSensorEnabled(enable && !dozing); + + if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) { + // Maximum brightness has changed, so recalculate display brightness. + mIsBrightnessThrottled = mBrightnessThrottler.isThrottled(); + changed = true; + } + if (changed) { updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange); } @@ -855,8 +867,11 @@ class AutomaticBrightnessController { // Clamps values with float range [0.0-1.0] private float clampScreenBrightness(float value) { - return MathUtils.constrain(value, - mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax()); + final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(), + mBrightnessThrottler.getBrightnessCap()); + final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(), + mBrightnessThrottler.getBrightnessCap()); + return MathUtils.constrain(value, minBrightness, maxBrightness); } private void prepareBrightnessAdjustmentSample() { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 3494342a66fb..6ae1a5a96a24 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -974,7 +974,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, screenBrightnessThresholds, mContext, - mHbmController, mIdleModeBrightnessMapper, + mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper, mDisplayDeviceConfig.getAmbientHorizonShort(), mDisplayDeviceConfig.getAmbientHorizonLong()); } else { @@ -1347,6 +1347,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } + // Now that a desired brightness has been calculated, apply brightness throttling. The + // dimming and low power transformations that follow can only dim brightness further. + // + // We didn't do this earlier through brightness clamping because we need to know both + // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations. + // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, + // we broadcast this change through setting. + final float unthrottledBrightnessState = brightnessState; + if (mBrightnessThrottler.isThrottled()) { + brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); + mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED); + if (!mAppliedThrottling) { + // Brightness throttling is needed, so do so quickly. + // Later, when throttling is removed, we let other mechanisms decide on speed. + slowChange = false; + updateScreenBrightnessSetting = true; + } + mAppliedThrottling = true; + } else if (mAppliedThrottling) { + mAppliedThrottling = false; + updateScreenBrightnessSetting = true; + } + if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this @@ -1393,20 +1416,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedLowPower = false; } - // Apply brightness throttling after applying all other transforms - final float unthrottledBrightnessState = brightnessState; - if (mBrightnessThrottler.isThrottled()) { - brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED); - if (!mAppliedThrottling) { - slowChange = false; - } - mAppliedThrottling = true; - } else if (mAppliedThrottling) { - slowChange = false; - mAppliedThrottling = false; - } - // The current brightness to use has been calculated at this point, and HbmController should // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it // here instead of having HbmController listen to the brightness setting because certain @@ -1656,6 +1665,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) { synchronized (mCachedBrightnessInfo) { + final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(), + mBrightnessThrottler.getBrightnessCap()); + final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(), + mBrightnessThrottler.getBrightnessCap()); boolean changed = false; changed |= @@ -1666,10 +1679,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call adjustedBrightness); changed |= mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin, - mHbmController.getCurrentBrightnessMin()); + minBrightness); changed |= mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax, - mHbmController.getCurrentBrightnessMax()); + maxBrightness); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, mHbmController.getHighBrightnessMode()); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 65ec1c07c4a1..4792821f652b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -385,6 +385,16 @@ public class HdmiCecConfig { R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed, R.bool.config_cecTvSendStandbyOnSleepDisabled_default); + Setting setMenuLanguage = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE, + R.bool.config_cecSetMenuLanguage_userConfigurable); + setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_ENABLED, + R.bool.config_cecSetMenuLanguageEnabled_allowed, + R.bool.config_cecSetMenuLanguageEnabled_default); + setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_DISABLED, + R.bool.config_cecSetMenuLanguageDisabled_allowed, + R.bool.config_cecSetMenuLanguageDisabled_default); + Setting rcProfileTv = registerSetting( HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, R.bool.config_cecRcProfileTv_userConfigurable); @@ -697,6 +707,8 @@ public class HdmiCecConfig { return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP: return STORAGE_SHARED_PREFS; + case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE: + return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV: return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU: @@ -768,6 +780,8 @@ public class HdmiCecConfig { return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP: return setting.getName(); + case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE: + return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV: return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU: diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index c674ffebfe92..454a76ac04f4 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -901,17 +901,12 @@ abstract class HdmiCecLocalDevice { protected int handleVendorCommandWithId(HdmiCecMessage message) { byte[] params = message.getParams(); int vendorId = HdmiUtils.threeBytesToInt(params); - if (vendorId == mService.getVendorId()) { - if (!mService.invokeVendorCommandListenersOnReceived( - mDeviceType, message.getSource(), message.getDestination(), params, true)) { - return Constants.ABORT_REFUSED; - } - } else if (message.getDestination() != Constants.ADDR_BROADCAST - && message.getSource() != Constants.ADDR_UNREGISTERED) { - Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>"); - return Constants.ABORT_UNRECOGNIZED_OPCODE; - } else { + if (message.getDestination() == Constants.ADDR_BROADCAST + || message.getSource() == Constants.ADDR_UNREGISTERED) { Slog.v(TAG, "Wrong broadcast vendor command. Ignoring"); + } else if (!mService.invokeVendorCommandListenersOnReceived( + mDeviceType, message.getSource(), message.getDestination(), params, true)) { + return Constants.ABORT_REFUSED; } return Constants.HANDLED; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index f413fbd5c9b2..0edcea548055 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -50,9 +50,6 @@ import java.util.Locale; public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { private static final String TAG = "HdmiCecLocalDevicePlayback"; - private static final boolean SET_MENU_LANGUAGE = - HdmiProperties.set_menu_language_enabled().orElse(false); - // How long to wait after hotplug out before possibly going to Standby. @VisibleForTesting static final long STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS = 30_000; @@ -388,7 +385,9 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @Constants.HandleMessageResult protected int handleSetMenuLanguage(HdmiCecMessage message) { assertRunOnServiceThread(); - if (!SET_MENU_LANGUAGE) { + if (mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE) + == HdmiControlManager.SET_MENU_LANGUAGE_DISABLED) { return Constants.ABORT_UNRECOGNIZED_OPCODE; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java index 6f7473d60121..57fe9e6a4acc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java @@ -133,6 +133,7 @@ public final class HdmiCecStandbyModeHandler { addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser); addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser); addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser); + addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser); addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8391e0b4e19a..8ac233114b48 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1701,11 +1701,11 @@ public class HdmiControlService extends SystemService { class VendorCommandListenerRecord implements IBinder.DeathRecipient { private final IHdmiVendorCommandListener mListener; - private final int mDeviceType; + private final int mVendorId; - public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) { + VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) { mListener = listener; - mDeviceType = deviceType; + mVendorId = vendorId; } @Override @@ -2191,10 +2191,10 @@ public class HdmiControlService extends SystemService { } @Override - public void addVendorCommandListener(final IHdmiVendorCommandListener listener, - final int deviceType) { + public void addVendorCommandListener( + final IHdmiVendorCommandListener listener, final int vendorId) { initBinderCall(); - HdmiControlService.this.addVendorCommandListener(listener, deviceType); + HdmiControlService.this.addVendorCommandListener(listener, vendorId); } @Override @@ -3354,8 +3354,9 @@ public class HdmiControlService extends SystemService { mStandbyMessageReceived = false; } - private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { - VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType); + @VisibleForTesting + void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) { + VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId); try { listener.asBinder().linkToDeath(record, 0); } catch (RemoteException e) { @@ -3374,8 +3375,14 @@ public class HdmiControlService extends SystemService { return false; } for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { - if (record.mDeviceType != deviceType) { - continue; + if (hasVendorId) { + int vendorId = + ((params[0] & 0xFF) << 16) + + ((params[1] & 0xFF) << 8) + + (params[2] & 0xFF); + if (record.mVendorId != vendorId) { + continue; + } } try { record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId); diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java new file mode 100644 index 000000000000..dbd3f3529208 --- /dev/null +++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + +import android.os.IBinder; +import android.view.InputApplicationHandle; +import android.view.InputChannel; +import android.view.InputMonitor; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * An internal implementation of an {@link InputMonitor} that uses a spy window. + * + * This spy window is a layer container in the SurfaceFlinger hierarchy that does not have any + * graphical buffer, but can still receive input. It is parented to the DisplayContent so + * that it can spy on any pointer events that start in the DisplayContent bounds. When the + * object is constructed, it will add itself to SurfaceFlinger. + */ +class GestureMonitorSpyWindow { + final InputApplicationHandle mApplicationHandle; + final InputWindowHandle mWindowHandle; + + // The token, InputChannel, and SurfaceControl are owned by this object. + final IBinder mMonitorToken; + final InputChannel mClientChannel; + final SurfaceControl mInputSurface; + + GestureMonitorSpyWindow(IBinder token, String name, int displayId, int pid, int uid, + SurfaceControl sc, InputChannel inputChannel) { + mMonitorToken = token; + mClientChannel = inputChannel; + mInputSurface = sc; + + mApplicationHandle = new InputApplicationHandle(null, name, + DEFAULT_DISPATCHING_TIMEOUT_MILLIS); + mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId); + + mWindowHandle.name = name; + mWindowHandle.token = mClientChannel.getToken(); + mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; + mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + mWindowHandle.visible = true; + mWindowHandle.focusable = false; + mWindowHandle.hasWallpaper = false; + mWindowHandle.paused = false; + mWindowHandle.ownerPid = pid; + mWindowHandle.ownerUid = uid; + mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY; + mWindowHandle.scaleFactor = 1.0f; + mWindowHandle.trustedOverlay = true; + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setInputWindowInfo(mInputSurface, mWindowHandle); + t.setLayer(mInputSurface, Integer.MAX_VALUE); + t.setPosition(mInputSurface, 0, 0); + t.setCrop(mInputSurface, null /* crop to parent surface */); + t.show(mInputSurface); + + t.apply(); + } + + void remove() { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.hide(mInputSurface); + t.remove(mInputSurface); + t.apply(); + + mClientChannel.dispose(); + } + + String dump() { + return "name='" + mWindowHandle.name + "', inputChannelToken=" + + mClientChannel.getToken() + " displayId=" + mWindowHandle.displayId; + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 783a88ca29bf..64b4da7c5bba 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -150,6 +150,8 @@ public class InputManagerService extends IInputManager.Stub static final String TAG = "InputManager"; static final boolean DEBUG = false; + private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = false; + private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml"; @@ -277,6 +279,12 @@ public class InputManagerService extends IInputManager.Stub @GuardedBy("mPointerDisplayIdLock") private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY; + + // Holds all the registered gesture monitors that are implemented as spy windows. The spy + // windows are mapped by their InputChannel tokens. + @GuardedBy("mInputMonitors") + final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>(); + private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); private static native void nativeStart(long ptr); @@ -716,33 +724,77 @@ public class InputManagerService extends IInputManager.Stub inputChannelName, Binder.getCallingPid()); } + @NonNull + private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name, + int displayId, int pid, int uid) { + final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name, + displayId); + if (sc == null) { + throw new IllegalArgumentException( + "Could not create gesture monitor surface on display: " + displayId); + } + final InputChannel channel = createInputChannel(name); + + try { + monitorToken.linkToDeath(() -> removeSpyWindowGestureMonitor(channel.getToken()), 0); + } catch (RemoteException e) { + Slog.i(TAG, "Client died before '" + name + "' could be created."); + return null; + } + synchronized (mInputMonitors) { + mInputMonitors.put(channel.getToken(), + new GestureMonitorSpyWindow(monitorToken, name, displayId, pid, uid, sc, + channel)); + } + + final InputChannel outInputChannel = new InputChannel(); + channel.copyTo(outInputChannel); + return outInputChannel; + } + + private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) { + final GestureMonitorSpyWindow monitor; + synchronized (mInputMonitors) { + monitor = mInputMonitors.remove(inputChannelToken); + } + removeInputChannel(inputChannelToken); + if (monitor == null) return; + monitor.remove(); + } + /** * Creates an input monitor that will receive pointer events for the purposes of system-wide * gesture interpretation. * - * @param inputChannelName The input channel name. + * @param requestedName The input channel name. * @param displayId Target display id. * @return The input channel. */ @Override // Binder call - public InputMonitor monitorGestureInput(String inputChannelName, int displayId) { + public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName, + int displayId) { if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, - "monitorInputRegion()")) { + "monitorGestureInput()")) { throw new SecurityException("Requires MONITOR_INPUT permission"); } - Objects.requireNonNull(inputChannelName, "inputChannelName must not be null."); + Objects.requireNonNull(requestedName, "name must not be null."); + Objects.requireNonNull(monitorToken, "token must not be null."); if (displayId < Display.DEFAULT_DISPLAY) { throw new IllegalArgumentException("displayId must >= 0."); } + final String name = "[Gesture Monitor] " + requestedName; final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - InputChannel inputChannel = nativeCreateInputMonitor( - mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid); - InputMonitorHost host = new InputMonitorHost(inputChannel.getToken()); - return new InputMonitor(inputChannel, host); + final InputChannel inputChannel = + USE_SPY_WINDOW_GESTURE_MONITORS + ? createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid) + : nativeCreateInputMonitor(mPtr, displayId, true /*isGestureMonitor*/, + requestedName, pid); + return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken())); } finally { Binder.restoreCallingIdentity(ident); } @@ -2563,37 +2615,51 @@ public class InputManagerService extends IInputManager.Stub String dumpStr = nativeDump(mPtr); if (dumpStr != null) { pw.println(dumpStr); - dumpAssociations(pw); } + + pw.println("Input Manager Service (Java) State:"); + dumpAssociations(pw, " " /*prefix*/); + dumpSpyWindowGestureMonitors(pw, " " /*prefix*/); } - private void dumpAssociations(PrintWriter pw) { + private void dumpAssociations(PrintWriter pw, String prefix) { if (!mStaticAssociations.isEmpty()) { - pw.println("Static Associations:"); + pw.println(prefix + "Static Associations:"); mStaticAssociations.forEach((k, v) -> { - pw.print(" port: " + k); + pw.print(prefix + " port: " + k); pw.println(" display: " + v); }); } synchronized (mAssociationsLock) { if (!mRuntimeAssociations.isEmpty()) { - pw.println("Runtime Associations:"); + pw.println(prefix + "Runtime Associations:"); mRuntimeAssociations.forEach((k, v) -> { - pw.print(" port: " + k); + pw.print(prefix + " port: " + k); pw.println(" display: " + v); }); } if (!mUniqueIdAssociations.isEmpty()) { - pw.println("Unique Id Associations:"); + pw.println(prefix + "Unique Id Associations:"); mUniqueIdAssociations.forEach((k, v) -> { - pw.print(" port: " + k); + pw.print(prefix + " port: " + k); pw.println(" uniqueId: " + v); }); } } } + private void dumpSpyWindowGestureMonitors(PrintWriter pw, String prefix) { + synchronized (mInputMonitors) { + if (mInputMonitors.isEmpty()) return; + pw.println(prefix + "Gesture Monitors (implemented as spy windows):"); + int i = 0; + for (final GestureMonitorSpyWindow monitor : mInputMonitors.values()) { + pw.append(prefix + " " + i++ + ": ").println(monitor.dump()); + } + } + } + private boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == Process.myPid()) { @@ -2618,6 +2684,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ } synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ } + synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ } nativeMonitor(mPtr); } @@ -2686,6 +2753,11 @@ public class InputManagerService extends IInputManager.Stub // Native callback. private void notifyInputChannelBroken(IBinder token) { + synchronized (mInputMonitors) { + if (mInputMonitors.containsKey(token)) { + removeSpyWindowGestureMonitor(token); + } + } mWindowManagerCallbacks.notifyInputChannelBroken(token); } @@ -2721,6 +2793,17 @@ public class InputManagerService extends IInputManager.Stub // Native callback private void notifyWindowUnresponsive(IBinder token, String reason) { + int gestureMonitorPid = -1; + synchronized (mInputMonitors) { + final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token); + if (gestureMonitor != null) { + gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid; + } + } + if (gestureMonitorPid != -1) { + mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason); + return; + } mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason); } @@ -2731,6 +2814,17 @@ public class InputManagerService extends IInputManager.Stub // Native callback private void notifyWindowResponsive(IBinder token) { + int gestureMonitorPid = -1; + synchronized (mInputMonitors) { + final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token); + if (gestureMonitor != null) { + gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid; + } + } + if (gestureMonitorPid != -1) { + mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid); + return; + } mWindowManagerCallbacks.notifyWindowResponsive(token); } @@ -3184,6 +3278,16 @@ public class InputManagerService extends IInputManager.Stub * pointers such as the mouse cursor and touch spots for the given display. */ SurfaceControl getParentSurfaceForPointers(int displayId); + + /** + * Create a {@link SurfaceControl} that can be configured to receive input over the entire + * display to implement a gesture monitor. The surface will not have a graphical buffer. + * @param name the name of the gesture monitor + * @param displayId the display to create the window in + * @return the SurfaceControl of the new layer container surface + */ + @Nullable + SurfaceControl createSurfaceForGestureMonitor(String name, int displayId); } /** @@ -3258,20 +3362,24 @@ public class InputManagerService extends IInputManager.Stub * Interface for the system to handle request from InputMonitors. */ private final class InputMonitorHost extends IInputMonitorHost.Stub { - private final IBinder mToken; + private final IBinder mInputChannelToken; - InputMonitorHost(IBinder token) { - mToken = token; + InputMonitorHost(IBinder inputChannelToken) { + mInputChannelToken = inputChannelToken; } @Override public void pilferPointers() { - nativePilferPointers(mPtr, mToken); + nativePilferPointers(mPtr, mInputChannelToken); } @Override public void dispose() { - nativeRemoveInputChannel(mPtr, mToken); + if (USE_SPY_WINDOW_GESTURE_MONITORS) { + removeSpyWindowGestureMonitor(mInputChannelToken); + return; + } + nativeRemoveInputChannel(mPtr, mInputChannelToken); } } diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java index 134fb967cac5..01aee7bc1942 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java +++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java @@ -31,13 +31,11 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.os.BestClock; import android.os.Binder; import android.os.HandlerThread; import android.os.LocaleList; import android.os.Process; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; @@ -61,7 +59,6 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Duration; -import java.time.ZoneOffset; import java.util.HashMap; /** @@ -97,15 +94,10 @@ class LocaleManagerBackupHelper { LocaleManagerBackupHelper(LocaleManagerService localeManagerService, PackageManagerInternal pmInternal) { - this(localeManagerService.mContext, localeManagerService, pmInternal, getDefaultClock(), + this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(), new SparseArray<>()); } - private static @NonNull Clock getDefaultClock() { - return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(), - Clock.systemUTC()); - } - @VisibleForTesting LocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) { diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index 21ea1f600b71..a1ee46b4c943 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -465,7 +465,7 @@ public abstract class IContextHubWrapper { try { mHub.onHostEndpointConnected(info); } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "RemoteException in onHostEndpointConnected"); + Log.e(TAG, "Exception in onHostEndpointConnected" + e.getMessage()); } } @@ -474,7 +474,7 @@ public abstract class IContextHubWrapper { try { mHub.onHostEndpointDisconnected((char) hostEndpointId); } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "RemoteException in onHostEndpointDisconnected"); + Log.e(TAG, "Exception in onHostEndpointDisconnected" + e.getMessage()); } } @@ -488,6 +488,8 @@ public abstract class IContextHubWrapper { return ContextHubTransaction.RESULT_SUCCESS; } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -499,8 +501,10 @@ public abstract class IContextHubWrapper { try { mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -510,8 +514,10 @@ public abstract class IContextHubWrapper { try { mHub.unloadNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -521,8 +527,10 @@ public abstract class IContextHubWrapper { try { mHub.enableNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -532,8 +540,10 @@ public abstract class IContextHubWrapper { try { mHub.disableNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -542,8 +552,10 @@ public abstract class IContextHubWrapper { try { mHub.queryNanoapps(contextHubId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; + } catch (IllegalArgumentException e) { + return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; } } @@ -551,7 +563,7 @@ public abstract class IContextHubWrapper { mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback)); try { mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId)); - } catch (RemoteException | ServiceSpecificException e) { + } catch (RemoteException | ServiceSpecificException | IllegalArgumentException e) { Log.e(TAG, "Exception while registering callback: " + e.getMessage()); } } diff --git a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java index 5036a6e7edf5..bfef97856838 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java @@ -63,16 +63,25 @@ class GnssNmeaProvider extends GnssListenerMultiplexer<Void, IGnssNmeaListener, @Override protected boolean registerWithService(Void ignored, Collection<GnssListenerRegistration> registrations) { - if (D) { - Log.d(TAG, "starting gnss nmea messages"); + if (mGnssNative.startNmeaMessageCollection()) { + if (D) { + Log.d(TAG, "starting gnss nmea messages collection"); + } + return true; + } else { + Log.e(TAG, "error starting gnss nmea messages collection"); + return false; } - return true; } @Override protected void unregisterWithService() { - if (D) { - Log.d(TAG, "stopping gnss nmea messages"); + if (mGnssNative.stopNmeaMessageCollection()) { + if (D) { + Log.d(TAG, "stopping gnss nmea messages collection"); + } + } else { + Log.e(TAG, "error stopping gnss nmea messages collection"); } } diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java index 936283deda8e..0ce36d6a8276 100644 --- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java @@ -43,6 +43,7 @@ public class GnssStatusProvider extends private final AppOpsHelper mAppOpsHelper; private final LocationUsageLogger mLogger; + private final GnssNative mGnssNative; private boolean mIsNavigating = false; @@ -50,6 +51,7 @@ public class GnssStatusProvider extends super(injector); mAppOpsHelper = injector.getAppOpsHelper(); mLogger = injector.getLocationUsageLogger(); + mGnssNative = gnssNative; gnssNative.addBaseCallbacks(this); gnssNative.addStatusCallbacks(this); @@ -64,16 +66,25 @@ public class GnssStatusProvider extends @Override protected boolean registerWithService(Void ignored, Collection<GnssListenerRegistration> registrations) { - if (D) { - Log.d(TAG, "starting gnss status"); + if (mGnssNative.startSvStatusCollection()) { + if (D) { + Log.d(TAG, "starting gnss sv status"); + } + return true; + } else { + Log.e(TAG, "error starting gnss sv status"); + return false; } - return true; } @Override protected void unregisterWithService() { - if (D) { - Log.d(TAG, "stopping gnss status"); + if (mGnssNative.stopSvStatusCollection()) { + if (D) { + Log.d(TAG, "stopping gnss sv status"); + } + } else { + Log.e(TAG, "error stopping gnss sv status"); } } diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java index e072bf7dc1f7..af87677ecb66 100644 --- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java +++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java @@ -783,6 +783,38 @@ public class GnssNative { } /** + * Starts sv status collection. + */ + public boolean startSvStatusCollection() { + Preconditions.checkState(mRegistered); + return mGnssHal.startSvStatusCollection(); + } + + /** + * Stops sv status collection. + */ + public boolean stopSvStatusCollection() { + Preconditions.checkState(mRegistered); + return mGnssHal.stopSvStatusCollection(); + } + + /** + * Starts NMEA message collection. + */ + public boolean startNmeaMessageCollection() { + Preconditions.checkState(mRegistered); + return mGnssHal.startNmeaMessageCollection(); + } + + /** + * Stops NMEA message collection. + */ + public boolean stopNmeaMessageCollection() { + Preconditions.checkState(mRegistered); + return mGnssHal.stopNmeaMessageCollection(); + } + + /** * Returns true if measurement corrections are supported. */ public boolean isMeasurementCorrectionsSupported() { @@ -1369,6 +1401,22 @@ public class GnssNative { return native_inject_measurement_corrections(corrections); } + protected boolean startSvStatusCollection() { + return native_start_sv_status_collection(); + } + + protected boolean stopSvStatusCollection() { + return native_stop_sv_status_collection(); + } + + protected boolean startNmeaMessageCollection() { + return native_start_nmea_message_collection(); + } + + protected boolean stopNmeaMessageCollection() { + return native_stop_nmea_message_collection(); + } + protected int getBatchSize() { return native_get_batch_size(); } @@ -1478,6 +1526,10 @@ public class GnssNative { private static native int native_read_nmea(byte[] buffer, int bufferSize); + private static native boolean native_start_nmea_message_collection(); + + private static native boolean native_stop_nmea_message_collection(); + // location injection APIs private static native void native_inject_location( @@ -1501,6 +1553,11 @@ public class GnssNative { private static native void native_inject_time(long time, long timeReference, int uncertainty); + // sv status APIs + private static native boolean native_start_sv_status_collection(); + + private static native boolean native_stop_sv_status_collection(); + // navigation message APIs private static native boolean native_is_navigation_message_supported(); diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java index 2fda02db63e5..06405ae32fcf 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java @@ -32,7 +32,6 @@ import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.parsing.ApkLiteParseUtils; import android.os.Environment; @@ -62,25 +61,14 @@ import java.util.concurrent.ExecutorService; * further cleanup and eventually all the installation/scanning related logic will go to another * class. */ -final class InitAppsHelper { +final class InitAndSystemPackageHelper { private final PackageManagerService mPm; + private final List<ScanPartition> mDirsToScanAsSystem; private final int mScanFlags; private final int mSystemParseFlags; private final int mSystemScanFlags; private final InstallPackageHelper mInstallPackageHelper; - private final ApexManager mApexManager; - private final PackageParser2 mPackageParser; - private final ExecutorService mExecutorService; - /* Tracks how long system scan took */ - private long mSystemScanTime; - /* Track of the number of cached system apps */ - private int mCachedSystemApps; - /* Track of the number of system apps */ - private int mSystemPackagesCount; - private final boolean mIsDeviceUpgrading; - private final boolean mIsOnlyCoreApps; - private final List<ScanPartition> mSystemPartitions; /** * Tracks new system packages [received in an OTA] that we expect to @@ -88,34 +76,21 @@ final class InitAppsHelper { * are package location. */ private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); - /* Tracks of any system packages that no longer exist that needs to be pruned. */ - private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>(); - // Tracks of stub packages that must either be replaced with full versions in the /data - // partition or be disabled. - private final List<String> mStubSystemApps = new ArrayList<>(); // TODO(b/198166813): remove PMS dependency - InitAppsHelper(PackageManagerService pm, ApexManager apexManager, - InstallPackageHelper installPackageHelper, PackageParser2 packageParser, - List<ScanPartition> systemPartitions) { + InitAndSystemPackageHelper(PackageManagerService pm) { mPm = pm; - mApexManager = apexManager; - mInstallPackageHelper = installPackageHelper; - mPackageParser = packageParser; - mSystemPartitions = systemPartitions; + mInstallPackageHelper = new InstallPackageHelper(pm); mDirsToScanAsSystem = getSystemScanPartitions(); - mIsDeviceUpgrading = mPm.isDeviceUpgrading(); - mIsOnlyCoreApps = mPm.isOnlyCoreApps(); // Set flag to monitor and not change apk file paths when scanning install directories. int scanFlags = SCAN_BOOTING | SCAN_INITIAL; - if (mIsDeviceUpgrading || mPm.isFirstBoot()) { + if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) { mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } else { mScanFlags = scanFlags; } mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM; - mExecutorService = ParallelPackageParser.makeExecutorService(); } private List<File> getFrameworkResApkSplitFiles() { @@ -143,7 +118,7 @@ final class InitAppsHelper { private List<ScanPartition> getSystemScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - scanPartitions.addAll(mSystemPartitions); + scanPartitions.addAll(mPm.mInjector.getSystemPartitions()); scanPartitions.addAll(getApexScanPartitions()); Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions); return scanPartitions; @@ -151,7 +126,8 @@ final class InitAppsHelper { private List<ScanPartition> getApexScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos(); + final List<ApexManager.ActiveApexInfo> activeApexInfos = + mPm.mApexManager.getActiveApexInfos(); for (int i = 0; i < activeApexInfos.size(); i++) { final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i)); if (scanPartition != null) { @@ -168,133 +144,117 @@ final class InitAppsHelper { if (apexInfo.preInstalledApexPath.getAbsolutePath().equals( sp.getFolder().getAbsolutePath()) || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath() + File.separator)) { + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } return null; } - /** - * Install apps from system dirs. - */ - @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) - public OverlayConfig initSystemApps(WatchedArrayMap<String, PackageSetting> packageSettings, - int[] userIds, long startTime) { + public OverlayConfig initPackages( + WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds, + long startTime) { + PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser(); + + ExecutorService executorService = ParallelPackageParser.makeExecutorService(); // Prepare apex package info before scanning APKs, this information is needed when // scanning apk in apex. - mApexManager.scanApexPackagesTraced(mPackageParser, mExecutorService); + mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService); - scanSystemDirs(mPackageParser, mExecutorService); + scanSystemDirs(packageParser, executorService); // Parse overlay configuration files to set default enable state, mutability, and // priority of system overlays. final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>(); - for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) { - for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) { + for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) { + for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) { apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath); } } - final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( + OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( consumer -> mPm.forEachPackage( pkg -> consumer.accept(pkg, pkg.isSystem(), - apkInApexPreInstalledPaths.get(pkg.getPackageName())))); - - if (!mIsOnlyCoreApps) { + apkInApexPreInstalledPaths.get(pkg.getPackageName())))); + // Prune any system packages that no longer exist. + final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>(); + // Stub packages must either be replaced with full versions in the /data + // partition or be disabled. + final List<String> stubSystemApps = new ArrayList<>(); + + if (!mPm.isOnlyCoreApps()) { // do this first before mucking with mPackages for the "expecting better" case - updateStubSystemAppsList(mStubSystemApps); + updateStubSystemAppsList(stubSystemApps); mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings, - mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); + possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); } - logSystemAppsScanningTime(startTime); - return overlayConfig; - } - - @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) - private void logSystemAppsScanningTime(long startTime) { - mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); + final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages mPm.mSettings.pruneSharedUsersLPw(); - mSystemScanTime = SystemClock.uptimeMillis() - startTime; - mSystemPackagesCount = mPm.mPackages.size(); - Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime - + " ms, packageCount: " + mSystemPackagesCount + final long systemScanTime = SystemClock.uptimeMillis() - startTime; + final int systemPackagesCount = mPm.mPackages.size(); + Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime + + " ms, packageCount: " + systemPackagesCount + " , timePerPackage: " - + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount) - + " , cached: " + mCachedSystemApps); - if (mIsDeviceUpgrading && mSystemPackagesCount > 0) { + + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) + + " , cached: " + cachedSystemApps); + if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) { //CHECKSTYLE:OFF IndentationCheck FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME, - mSystemScanTime / mSystemPackagesCount); + systemScanTime / systemPackagesCount); //CHECKSTYLE:ON IndentationCheck } - } - /** - * Install apps/updates from data dir and fix system apps that are affected. - */ - @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) - public void initNonSystemApps(@NonNull int[] userIds, long startTime) { - if (!mIsOnlyCoreApps) { + if (!mPm.isOnlyCoreApps()) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0, - mScanFlags | SCAN_REQUIRE_KNOWN, - mPackageParser, mExecutorService); + mScanFlags | SCAN_REQUIRE_KNOWN, 0, + packageParser, executorService); + } - List<Runnable> unfinishedTasks = mExecutorService.shutdownNow(); + List<Runnable> unfinishedTasks = executorService.shutdownNow(); if (!unfinishedTasks.isEmpty()) { throw new IllegalStateException("Not all tasks finished before calling close: " + unfinishedTasks); } - if (!mIsOnlyCoreApps) { - fixSystemPackages(userIds); - logNonSystemAppScanningTime(startTime); + + if (!mPm.isOnlyCoreApps()) { + mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps, + userIds, mScanFlags); + mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, + stubSystemApps, mSystemScanFlags, mSystemParseFlags); + + // Uncompress and install any stubbed system applications. + // This must be done last to ensure all stubs are replaced or disabled. + mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags); + + final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() + - cachedSystemApps; + + final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; + final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount; + Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + + " ms, packageCount: " + dataPackagesCount + + " , timePerPackage: " + + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + + " , cached: " + cachedNonSystemApps); + if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) { + //CHECKSTYLE:OFF IndentationCheck + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, + dataScanTime / dataPackagesCount); + //CHECKSTYLE:OFF IndentationCheck + } } mExpectingBetter.clear(); - mPm.mSettings.pruneRenamedPackagesLPw(); - mPackageParser.close(); - } - - /** - * Clean up system packages now that some system package updates have been installed from - * the data dir. Also install system stub packages as the last step. - */ - @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) - private void fixSystemPackages(@NonNull int[] userIds) { - mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps, - userIds, mScanFlags); - mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, - mStubSystemApps, mSystemScanFlags, mSystemParseFlags); - - // Uncompress and install any stubbed system applications. - // This must be done last to ensure all stubs are replaced or disabled. - mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags); - } - @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) - private void logNonSystemAppScanningTime(long startTime) { - final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() - - mCachedSystemApps; - - final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime; - final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount; - Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime - + " ms, packageCount: " + dataPackagesCount - + " , timePerPackage: " - + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) - + " , cached: " + cachedNonSystemApps); - if (mIsDeviceUpgrading && dataPackagesCount > 0) { - //CHECKSTYLE:OFF IndentationCheck - FrameworkStatsLog.write( - FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, - BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, - dataScanTime / dataPackagesCount); - //CHECKSTYLE:OFF IndentationCheck - } + mPm.mSettings.pruneRenamedPackagesLPw(); + packageParser.close(); + return overlayConfig; } /** @@ -314,14 +274,14 @@ final class InitAppsHelper { continue; } scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, packageParser, executorService); } List<File> frameworkSplits = getFrameworkResApkSplitFiles(); scanDirTracedLI(frameworkDir, frameworkSplits, mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS, - mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, + mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { throw new IllegalStateException( @@ -333,11 +293,11 @@ final class InitAppsHelper { if (partition.getPrivAppFolder() != null) { scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null, mSystemParseFlags, - mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, + mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0, packageParser, executorService); } scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, packageParser, executorService); } } @@ -356,11 +316,11 @@ final class InitAppsHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private void scanDirTracedLI(File scanDir, List<File> frameworkSplits, final int parseFlags, int scanFlags, - PackageParser2 packageParser, ExecutorService executorService) { + long currentTime, PackageParser2 packageParser, ExecutorService executorService) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags, - scanFlags, packageParser, executorService); + scanFlags, currentTime, packageParser, executorService); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 83c84728cb5a..336da2acca67 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3030,7 +3030,7 @@ final class InstallPackageHelper { final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); removePackageHelper.removePackageLI(stubPkg, true /*chatty*/); try { - return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null); + return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), e); @@ -3163,7 +3163,7 @@ final class InstallPackageHelper { | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); final AndroidPackage pkg = scanSystemPackageTracedLI( - codePath, parseFlags, scanFlags, null); + codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); @@ -3338,7 +3338,7 @@ final class InstallPackageHelper { mRemovePackageHelper.removePackageLI(pkg, true); try { final File codePath = new File(pkg.getPath()); - scanSystemPackageTracedLI(codePath, 0, scanFlags, null); + scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); @@ -3359,7 +3359,7 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags, - int scanFlags, PackageParser2 packageParser, + int scanFlags, long currentTime, PackageParser2 packageParser, ExecutorService executorService) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { @@ -3402,7 +3402,7 @@ final class InstallPackageHelper { parseResult.parsedPackage); } try { - addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, + addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; @@ -3465,7 +3465,7 @@ final class InstallPackageHelper { try { final AndroidPackage newPkg = scanSystemPackageTracedLI( - scanFile, reparseFlags, rescanFlags, null); + scanFile, reparseFlags, rescanFlags, 0, null); // We rescanned a stub, add it to the list of stubbed system packages if (newPkg.isStub()) { stubSystemApps.add(packageName); @@ -3479,14 +3479,14 @@ final class InstallPackageHelper { /** * Traces a package scan. - * @see #scanSystemPackageLI(File, int, int, UserHandle) + * @see #scanSystemPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags, - int scanFlags, UserHandle user) throws PackageManagerException { + int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); try { - return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user); + return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3498,7 +3498,7 @@ final class InstallPackageHelper { */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags, - UserHandle user) throws PackageManagerException { + long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); @@ -3514,7 +3514,7 @@ final class InstallPackageHelper { PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); } - return addForInitLI(parsedPackage, parseFlags, scanFlags, user); + return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user); } /** @@ -3533,11 +3533,11 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) private AndroidPackage addForInitLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, + @PackageManagerService.ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI( - parsedPackage, parseFlags, scanFlags, user); + parsedPackage, parseFlags, scanFlags, currentTime, user); final ScanResult scanResult = scanResultPair.first; boolean shouldHideSystemApp = scanResultPair.second; if (scanResult.mSuccess) { @@ -3715,7 +3715,7 @@ final class InstallPackageHelper { private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, + @PackageManagerService.ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0; @@ -3902,7 +3902,7 @@ final class InstallPackageHelper { } final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, - scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null); + scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null); return new Pair<>(scanResult, shouldHideSystemApp); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9d95ead3a311..ee5c6385429a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -247,6 +247,7 @@ import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; import com.android.server.storage.DeviceStorageMonitorInternal; +import com.android.server.supplementalprocess.SupplementalProcessManagerLocal; import com.android.server.utils.SnapshotCache; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; @@ -934,6 +935,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mOverlayConfigSignaturePackage; final @Nullable String mRecentsPackage; final @Nullable String mAmbientContextDetectionPackage; + private final @NonNull String mRequiredSupplementalProcessPackage; @GuardedBy("mLock") private final PackageUsage mPackageUsage = new PackageUsage(); @@ -944,7 +946,7 @@ public class PackageManagerService extends IPackageManager.Stub private final BroadcastHelper mBroadcastHelper; private final RemovePackageHelper mRemovePackageHelper; private final DeletePackageHelper mDeletePackageHelper; - private final InitAppsHelper mInitAppsHelper; + private final InitAndSystemPackageHelper mInitAndSystemPackageHelper; private final AppDataHelper mAppDataHelper; private final InstallPackageHelper mInstallPackageHelper; private final PreferredActivityHelper mPreferredActivityHelper; @@ -1671,6 +1673,7 @@ public class PackageManagerService extends IPackageManager.Stub mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName; mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage; mResolveComponentName = testParams.resolveComponentName; + mRequiredSupplementalProcessPackage = testParams.requiredSupplementalProcessPackage; mLiveComputer = createLiveComputer(); mSnapshotComputer = null; @@ -1689,12 +1692,13 @@ public class PackageManagerService extends IPackageManager.Stub mAppDataHelper = testParams.appDataHelper; mInstallPackageHelper = testParams.installPackageHelper; mRemovePackageHelper = testParams.removePackageHelper; - mInitAppsHelper = testParams.initAndSystemPackageHelper; + mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper; mDeletePackageHelper = testParams.deletePackageHelper; mPreferredActivityHelper = testParams.preferredActivityHelper; mResolveIntentHelper = testParams.resolveIntentHelper; mDexOptHelper = testParams.dexOptHelper; mSuspendPackageHelper = testParams.suspendPackageHelper; + mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); registerObservers(false); @@ -1836,8 +1840,7 @@ public class PackageManagerService extends IPackageManager.Stub mAppDataHelper = new AppDataHelper(this); mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper); mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper); - mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, - mInjector.getScanningCachingPackageParser(), mInjector.getSystemPartitions()); + mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this); mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, mAppDataHelper); mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); @@ -1967,8 +1970,8 @@ public class PackageManagerService extends IPackageManager.Stub mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion); final int[] userIds = mUserManager.getUserIds(); - mOverlayConfig = mInitAppsHelper.initSystemApps(packageSettings, userIds, startTime); - mInitAppsHelper.initNonSystemApps(userIds, startTime); + mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings, + userIds, startTime); // Resolve the storage manager. mStorageManagerPackage = getStorageManagerPackageName(); @@ -2139,6 +2142,9 @@ public class PackageManagerService extends IPackageManager.Stub getPackageInfo(mRequiredPermissionControllerPackage, 0, UserHandle.USER_SYSTEM).getLongVersionCode()); + // Resolve the supplemental process + mRequiredSupplementalProcessPackage = getRequiredSupplementalProcessPackageName(); + // Initialize InstantAppRegistry's Instant App list for all users. for (AndroidPackage pkg : mPackages.values()) { if (pkg.isSystem()) { @@ -3154,6 +3160,11 @@ public class PackageManagerService extends IPackageManager.Stub throw new IllegalStateException("PermissionController is not found"); } + @Override + public String getSupplementalProcessPackageName() { + return mRequiredSupplementalProcessPackage; + } + String getPackageInstallerPackageName() { return mRequiredInstallerPackage; } @@ -5519,6 +5530,24 @@ public class PackageManagerService extends IPackageManager.Stub } } + private @NonNull String getRequiredSupplementalProcessPackageName() { + final Intent intent = new Intent(SupplementalProcessManagerLocal.SERVICE_INTERFACE); + + final List<ResolveInfo> matches = queryIntentServicesInternal( + intent, + /* resolvedType= */ null, + MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.USER_SYSTEM, + /* callingUid= */ Process.myUid(), + /* includeInstantApps= */ false); + if (matches.size() == 1) { + return matches.get(0).getComponentInfo().packageName; + } else { + throw new RuntimeException("There should exactly one supplemental process; found " + + matches.size() + ": matches=" + matches); + } + } + @Override public String getDefaultTextClassifierPackageName() { return ensureSystemPackageName( @@ -8677,7 +8706,7 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isExpectingBetter(String packageName) { - return mInitAppsHelper.isExpectingBetter(packageName); + return mInitAndSystemPackageHelper.isExpectingBetter(packageName); } int getDefParseFlags() { @@ -8780,12 +8809,13 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isOverlayMutable(String packageName) { - return mOverlayConfig.isMutable(packageName); + return (mOverlayConfig != null ? mOverlayConfig + : OverlayConfig.getSystemInstance()).isMutable(packageName); } @ScanFlags int getSystemPackageScanFlags(File codePath) { List<ScanPartition> dirsToScanAsSystem = - mInitAppsHelper.getDirsToScanAsSystem(); + mInitAndSystemPackageHelper.getDirsToScanAsSystem(); @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) { ScanPartition partition = dirsToScanAsSystem.get(i); @@ -8803,7 +8833,7 @@ public class PackageManagerService extends IPackageManager.Stub Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile, int systemScanFlags, int systemParseFlags) { List<ScanPartition> dirsToScanAsSystem = - mInitAppsHelper.getDirsToScanAsSystem(); + mInitAndSystemPackageHelper.getDirsToScanAsSystem(); @ParsingPackageUtils.ParseFlags int reparseFlags = 0; @PackageManagerService.ScanFlags int rescanFlags = 0; for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 00ca4ae8b734..d12c826b8d40 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -89,6 +89,7 @@ public final class PackageManagerServiceTestParams { public @Nullable String defaultTextClassifierPackage; public @Nullable String systemTextClassifierPackage; public @Nullable String overlayConfigSignaturePackage; + public @NonNull String requiredSupplementalProcessPackage; public ViewCompiler viewCompiler; public @Nullable String retailDemoPackage; public @Nullable String recentsPackage; @@ -108,7 +109,7 @@ public final class PackageManagerServiceTestParams { public AppDataHelper appDataHelper; public InstallPackageHelper installPackageHelper; public RemovePackageHelper removePackageHelper; - public InitAppsHelper initAndSystemPackageHelper; + public InitAndSystemPackageHelper initAndSystemPackageHelper; public DeletePackageHelper deletePackageHelper; public PreferredActivityHelper preferredActivityHelper; public ResolveIntentHelper resolveIntentHelper; diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 137209850736..bb7e55a4bf40 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -32,6 +32,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import android.os.Environment; import android.os.FileUtils; import android.os.UserHandle; @@ -47,7 +48,6 @@ import android.util.Slog; import com.android.internal.policy.AttributeCache; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.util.ArrayList; @@ -150,7 +150,7 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg; try { pkg = installPackageHelper.scanSystemPackageTracedLI( - ps.getPath(), parseFlags, SCAN_INITIAL, null); + ps.getPath(), parseFlags, SCAN_INITIAL, 0, null); loaded.add(pkg); } catch (PackageManagerException e) { diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 06ce4a41afec..1dea3d7943d8 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -71,6 +71,7 @@ public class TrustAgentWrapper { private static final int MSG_ESCROW_TOKEN_STATE = 9; private static final int MSG_UNLOCK_USER = 10; private static final int MSG_SHOW_KEYGUARD_ERROR_MESSAGE = 11; + private static final int MSG_LOCK_USER = 12; /** * Time in uptime millis that we wait for the service connection, both when starting @@ -100,6 +101,8 @@ public class TrustAgentWrapper { // Trust state private boolean mTrusted; + private boolean mWaitingForTrustableDowngrade = false; + private boolean mTrustable; private CharSequence mMessage; private boolean mDisplayTrustGrantedMessage; private boolean mTrustDisabledByDpm; @@ -108,6 +111,25 @@ public class TrustAgentWrapper { private AlarmManager mAlarmManager; private final Intent mAlarmIntent; private PendingIntent mAlarmPendingIntent; + private final BroadcastReceiver mTrustableDowngradeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) { + return; + } + if (!mWaitingForTrustableDowngrade) { + return; + } + // are these the broadcasts we want to listen to + if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction()) + || Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { + mTrusted = false; + mTrustable = true; + mWaitingForTrustableDowngrade = false; + mTrustManagerService.updateTrust(mUserId, 0); + } + } + }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -126,16 +148,21 @@ public class TrustAgentWrapper { public void handleMessage(Message msg) { switch (msg.what) { case MSG_GRANT_TRUST: - // TODO(b/213631675): Respect FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE if (!isConnected()) { Log.w(TAG, "Agent is not connected, cannot grant trust: " + mName.flattenToShortString()); return; } mTrusted = true; + mTrustable = false; mMessage = (CharSequence) msg.obj; int flags = msg.arg1; mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0; + if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) { + mWaitingForTrustableDowngrade = true; + } else { + mWaitingForTrustableDowngrade = false; + } long durationMs = msg.getData().getLong(DATA_DURATION); if (durationMs > 0) { final long duration; @@ -270,6 +297,13 @@ public class TrustAgentWrapper { mTrustManagerService.showKeyguardErrorMessage(message); break; } + case MSG_LOCK_USER: { + mTrusted = false; + mTrustable = false; + mTrustManagerService.updateTrust(mUserId, 0 /* flags */); + mTrustManagerService.lockUser(mUserId); + break; + } } } }; @@ -295,6 +329,11 @@ public class TrustAgentWrapper { } @Override + public void lockUser() { + mHandler.sendEmptyMessage(MSG_LOCK_USER); + } + + @Override public void setManagingTrust(boolean managingTrust) { if (DEBUG) Slog.d(TAG, "managingTrust()"); mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget(); @@ -427,6 +466,9 @@ public class TrustAgentWrapper { final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME); alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL); + IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_USER_PRESENT); + trustableFilter.addAction(Intent.ACTION_SCREEN_OFF); + // Schedules a restart for when connecting times out. If the connection succeeds, // the restart is canceled in mCallback's onConnected. scheduleRestart(); @@ -435,6 +477,7 @@ public class TrustAgentWrapper { if (mBound) { mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null, Context.RECEIVER_EXPORTED); + mContext.registerReceiver(mTrustableDowngradeReceiver, trustableFilter); } else { Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString()); } @@ -591,6 +634,10 @@ public class TrustAgentWrapper { return mTrusted && mManagingTrust && !mTrustDisabledByDpm; } + public boolean isTrustable() { + return mTrustable && mManagingTrust && !mTrustDisabledByDpm; + } + public boolean isManagingTrust() { return mManagingTrust && !mTrustDisabledByDpm; } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 52f7d101ce99..6aafd4aec0ab 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -54,6 +54,7 @@ import android.os.Message; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -65,6 +66,7 @@ import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.Xml; import android.view.IWindowManager; @@ -122,8 +124,7 @@ public class TrustManagerService extends SystemService { private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13; private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14; private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15; - private static final int MSG_USER_REQUESTED_UNLOCK = 16; - private static final int MSG_ENABLE_TRUST_AGENT = 17; + public static final int MSG_USER_REQUESTED_UNLOCK = 16; private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except"; @@ -146,6 +147,21 @@ public class TrustManagerService extends SystemService { @GuardedBy("mUserIsTrusted") private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray(); + //TODO(b/215724686): remove flag + public static final boolean ENABLE_ACTIVE_UNLOCK_FLAG = SystemProperties.getBoolean( + "fw.enable_active_unlock_flag", true); + + private enum TrustState { + UNTRUSTED, // the phone is not unlocked by any trustagents + TRUSTABLE, // the phone is in a semi-locked state that can be unlocked if + // FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE is passed and a trustagent is trusted + TRUSTED // the phone is unlocked + }; + + @GuardedBy("mUserTrustState") + private final SparseArray<TrustManagerService.TrustState> mUserTrustState = + new SparseArray<>(); + /** * Stores the locked state for users on the device. There are three different type of users * which are handled slightly differently: @@ -229,7 +245,6 @@ public class TrustManagerService extends SystemService { } // Extend unlock config and logic - private final class SettingsObserver extends ContentObserver { private final Uri TRUST_AGENTS_EXTEND_UNLOCK = Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK); @@ -397,6 +412,14 @@ public class TrustManagerService extends SystemService { } private void updateTrust(int userId, int flags, boolean isFromUnlock) { + if (ENABLE_ACTIVE_UNLOCK_FLAG) { + updateTrustWithRenewableUnlock(userId, flags, isFromUnlock); + } else { + updateTrustWithExtendUnlock(userId, flags, isFromUnlock); + } + } + + private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) { boolean managed = aggregateIsTrustManaged(userId); dispatchOnTrustManagedChanged(managed, userId); if (mStrongAuthTracker.isTrustAllowedForUser(userId) @@ -442,6 +465,65 @@ public class TrustManagerService extends SystemService { } } + private void updateTrustWithRenewableUnlock(int userId, int flags, boolean isFromUnlock) { + boolean managed = aggregateIsTrustManaged(userId); + dispatchOnTrustManagedChanged(managed, userId); + if (mStrongAuthTracker.isTrustAllowedForUser(userId) + && isTrustUsuallyManagedInternal(userId) != managed) { + updateTrustUsuallyManaged(userId, managed); + } + + boolean trustedByAtLeastOneAgent = aggregateIsTrusted(userId); + boolean trustableByAtLeastOneAgent = aggregateIsTrustable(userId); + boolean wasTrusted; + boolean wasTrustable; + TrustState pendingTrustState; + + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + boolean alreadyUnlocked = false; + try { + alreadyUnlocked = !wm.isKeyguardLocked(); + } catch (RemoteException e) { + } + + synchronized (mUserTrustState) { + wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED); + wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE); + boolean renewingTrust = wasTrustable && ( + (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0); + boolean canMoveToTrusted = alreadyUnlocked || isFromUnlock || renewingTrust; + boolean upgradingTrustForCurrentUser = (userId == mCurrentUser); + + if (trustedByAtLeastOneAgent && wasTrusted) { + // no change + return; + } else if (trustedByAtLeastOneAgent && canMoveToTrusted + && upgradingTrustForCurrentUser) { + pendingTrustState = TrustState.TRUSTED; + } else if (trustableByAtLeastOneAgent && (wasTrusted || wasTrustable) + && upgradingTrustForCurrentUser) { + pendingTrustState = TrustState.TRUSTABLE; + } else { + pendingTrustState = TrustState.UNTRUSTED; + } + + mUserTrustState.put(userId, pendingTrustState); + } + if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState); + + boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED; + dispatchOnTrustChanged(isNowTrusted, userId, flags, getTrustGrantedMessages(userId)); + if (isNowTrusted != wasTrusted) { + refreshDeviceLockedForUser(userId); + if (!isNowTrusted) { + maybeLockScreen(userId); + } else { + scheduleTrustTimeout(userId, false /* override */); + } + } + } + + private void updateTrustUsuallyManaged(int userId, boolean managed) { synchronized (mTrustUsuallyManagedForUser) { mTrustUsuallyManagedForUser.put(userId, managed); @@ -473,6 +555,20 @@ public class TrustManagerService extends SystemService { mLockPatternUtils.unlockUserWithToken(handle, token, userId); } + /** + * Locks the phone and requires some auth (not trust) like a biometric or passcode before + * unlocking. + */ + public void lockUser(int userId) { + mLockPatternUtils.requireStrongAuth( + StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); + try { + WindowManagerGlobal.getWindowManagerService().lockNow(null); + } catch (RemoteException e) { + Slog.e(TAG, "Error locking screen when called from trust agent"); + } + } + void showKeyguardErrorMessage(CharSequence message) { dispatchOnTrustError(message); } @@ -632,24 +728,6 @@ public class TrustManagerService extends SystemService { } } - - /** - * Uses {@link LockPatternUtils} to enable the setting for trust agent in the specified - * component name. This should only be used for testing. - */ - private void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) { - Log.i(TAG, - "Enabling trust agent " + componentName.flattenToString() + " for user " + userId); - List<ComponentName> agents = - new ArrayList<>(mLockPatternUtils.getEnabledTrustAgents(userId)); - if (!agents.contains(componentName)) { - agents.add(componentName); - } - // Even if the agent was already there, we still call setEnabledTrustAgents to trigger a - // refresh of installed agents. - mLockPatternUtils.setEnabledTrustAgents(agents, userId); - } - boolean isDeviceLockedInner(int userId) { synchronized (mDeviceLockedForUser) { return mDeviceLockedForUser.get(userId, true); @@ -948,7 +1026,6 @@ public class TrustManagerService extends SystemService { continue; } allowedAgents.add(resolveInfo); - if (DEBUG) Slog.d(TAG, "Adding agent " + getComponentName(resolveInfo)); } return allowedAgents; } @@ -970,6 +1047,21 @@ public class TrustManagerService extends SystemService { return false; } + private boolean aggregateIsTrustable(int userId) { + if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) { + return false; + } + for (int i = 0; i < mActiveAgents.size(); i++) { + AgentInfo info = mActiveAgents.valueAt(i); + if (info.userId == userId) { + if (info.agent.isTrustable()) { + return true; + } + } + } + return false; + } + private List<String> getTrustGrantedMessages(int userId) { if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) { return new ArrayList<>(); @@ -1178,13 +1270,6 @@ public class TrustManagerService extends SystemService { } @Override - public void enableTrustAgentForUserForTest(ComponentName componentName, int userId) - throws RemoteException { - enforceReportPermission(); - mHandler.obtainMessage(MSG_ENABLE_TRUST_AGENT, userId, 0, componentName).sendToTarget(); - } - - @Override public void reportKeyguardShowingChanged() throws RemoteException { enforceReportPermission(); // coalesce refresh messages. @@ -1460,9 +1545,6 @@ public class TrustManagerService extends SystemService { // This is also called when the security mode of a user changes. refreshDeviceLockedForUser(UserHandle.USER_ALL); break; - case MSG_ENABLE_TRUST_AGENT: - enableTrustAgentForUserForTest((ComponentName) msg.obj, msg.arg1); - break; case MSG_KEYGUARD_SHOWING_CHANGED: refreshDeviceLockedForUser(mCurrentUser); break; diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index f91969b2c558..1f0fdcf0a8d2 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -24,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.PointF; import android.os.Debug; import android.os.IBinder; @@ -254,6 +255,25 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal } } + @Override + @Nullable + public SurfaceControl createSurfaceForGestureMonitor(String name, int displayId) { + synchronized (mService.mGlobalLock) { + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + if (dc == null) { + Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId + + " - DisplayContent not found."); + return null; + } + return mService.makeSurfaceBuilder(dc.getSession()) + .setContainerLayer() + .setName(name) + .setCallsite("createSurfaceForGestureMonitor") + .setParent(dc.getSurfaceControl()) + .build(); + } + } + /** Waits until the built-in input devices have been configured. */ public boolean waitForInputDevicesReady(long timeoutMillis) { synchronized (mInputDevicesReadyMonitor) { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 10776abee51e..1e121737e2ac 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_ import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityTaskManager; import android.app.StatusBarManager; import android.graphics.Insets; import android.graphics.Rect; @@ -134,7 +135,7 @@ class InsetsPolicy { /** Updates the target which can control system bars. */ void updateBarControlTarget(@Nullable WindowState focusedWin) { - if (mFocusedWin != focusedWin){ + if (mFocusedWin != focusedWin) { abortTransient(); } mFocusedWin = focusedWin; @@ -156,7 +157,7 @@ class InsetsPolicy { } boolean isHidden(@InternalInsetsType int type) { - final InsetsSourceProvider provider = mStateController.peekSourceProvider(type); + final InsetsSourceProvider provider = mStateController.peekSourceProvider(type); return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); } @@ -181,6 +182,10 @@ class InsetsPolicy { mShowingTransientTypes.toArray(), isGestureOnSystemBar); } updateBarControlTarget(mFocusedWin); + dispatchTransientSystemBarsVisibilityChanged( + mFocusedWin, + isTransient(ITYPE_STATUS_BAR) || isTransient(ITYPE_NAVIGATION_BAR), + isGestureOnSystemBar); // The leashes can be created while updating bar control target. The surface transaction // of the new leashes might not be applied yet. The callback posted here ensures we can @@ -198,6 +203,12 @@ class InsetsPolicy { if (mShowingTransientTypes.size() == 0) { return; } + + dispatchTransientSystemBarsVisibilityChanged( + mFocusedWin, + /* areVisible= */ false, + /* wereRevealedFromSwipeOnSystemBar= */ false); + startAnimation(false /* show */, () -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { @@ -373,6 +384,11 @@ class InsetsPolicy { mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray()); } mShowingTransientTypes.clear(); + + dispatchTransientSystemBarsVisibilityChanged( + mFocusedWin, + /* areVisible= */ false, + /* wereRevealedFromSwipeOnSystemBar= */ false); } private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, @@ -521,6 +537,32 @@ class InsetsPolicy { listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); } + private void dispatchTransientSystemBarsVisibilityChanged( + @Nullable WindowState focusedWindow, + boolean areVisible, + boolean wereRevealedFromSwipeOnSystemBar) { + if (focusedWindow == null) { + return; + } + + Task task = focusedWindow.getTask(); + if (task == null) { + return; + } + + int taskId = task.mTaskId; + boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID; + if (!isValidTaskId) { + return; + } + + mDisplayContent.mWmService.mTaskSystemBarsListenerController + .dispatchTransientSystemBarVisibilityChanged( + taskId, + areVisible, + wereRevealedFromSwipeOnSystemBar); + } + private class BarWindow { private final int mId; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 76a7981fa946..ee03d02791ff 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -981,6 +981,29 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.checkDrawnWindowsLocked(); } + final int N = mWmService.mPendingRemove.size(); + if (N > 0) { + if (mWmService.mPendingRemoveTmp.length < N) { + mWmService.mPendingRemoveTmp = new WindowState[N + 10]; + } + mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp); + mWmService.mPendingRemove.clear(); + ArrayList<DisplayContent> displayList = new ArrayList(); + for (i = 0; i < N; i++) { + final WindowState w = mWmService.mPendingRemoveTmp[i]; + w.removeImmediately(); + final DisplayContent displayContent = w.getDisplayContent(); + if (displayContent != null && !displayList.contains(displayContent)) { + displayList.add(displayContent); + } + } + + for (int j = displayList.size() - 1; j >= 0; --j) { + final DisplayContent dc = displayList.get(j); + dc.assignWindowLayers(true /*setLayoutNeeded*/); + } + } + forAllDisplays(dc -> { dc.getInputMonitor().updateInputWindowsLw(true /*force*/); dc.updateSystemGestureExclusion(); diff --git a/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java new file mode 100644 index 000000000000..acb6061de93f --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import com.android.internal.os.BackgroundThread; +import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; + +import java.util.HashSet; +import java.util.concurrent.Executor; + +/** + * Manages dispatch of task system bar changes to interested listeners. All invocations must be + * performed while the {@link WindowManagerService#getWindowManagerLock() Window Manager Lock} is + * held. + */ +final class TaskSystemBarsListenerController { + + private final HashSet<TaskSystemBarsListener> mListeners = new HashSet<>(); + private final Executor mBackgroundExecutor; + + TaskSystemBarsListenerController() { + this.mBackgroundExecutor = BackgroundThread.getExecutor(); + } + + void registerListener(TaskSystemBarsListener listener) { + mListeners.add(listener); + } + + void unregisterListener(TaskSystemBarsListener listener) { + mListeners.remove(listener); + } + + void dispatchTransientSystemBarVisibilityChanged( + int taskId, + boolean visible, + boolean wereRevealedFromSwipeOnSystemBar) { + HashSet<TaskSystemBarsListener> localListeners; + localListeners = new HashSet<>(mListeners); + + mBackgroundExecutor.execute(() -> { + for (TaskSystemBarsListener listener : localListeners) { + listener.onTransientSystemBarsVisibilityChanged( + taskId, + visible, + wereRevealedFromSwipeOnSystemBar); + } + }); + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 20fa7a9da256..4900f9292f2a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -256,6 +256,25 @@ public abstract class WindowManagerInternal { } /** + * An interface to be notified when the system bars for a task change. + */ + public interface TaskSystemBarsListener { + + /** + * Called when the visibility of the system bars of a task change. + * + * @param taskId the identifier of the task. + * @param visible if the transient system bars are visible. + * @param wereRevealedFromSwipeOnSystemBar if the transient bars were revealed due to a + * swipe gesture on a system bar. + */ + void onTransientSystemBarsVisibilityChanged( + int taskId, + boolean visible, + boolean wereRevealedFromSwipeOnSystemBar); + } + + /** * An interface to be notified when keyguard exit animation should start. */ public interface KeyguardExitAnimationStartListener { @@ -519,6 +538,20 @@ public abstract class WindowManagerInternal { public abstract void registerAppTransitionListener(AppTransitionListener listener); /** + * Registers a listener to be notified to when the system bars of a task changes. + * + * @param listener The listener to register. + */ + public abstract void registerTaskSystemBarsListener(TaskSystemBarsListener listener); + + /** + * Registers a listener to be notified to when the system bars of a task changes. + * + * @param listener The listener to unregister. + */ + public abstract void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener); + + /** * Registers a listener to be notified to start the keyguard exit animation. * * @param listener The listener to register. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b37cb4f286f9..1167cb531c76 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -222,6 +222,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.MergedConfiguration; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.TypedValue; @@ -586,6 +587,20 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mResizingWindows = new ArrayList<>(); /** + * Windows whose animations have ended and now must be removed. + */ + final ArrayList<WindowState> mPendingRemove = new ArrayList<>(); + + /** + * Used when processing mPendingRemove to avoid working on the original array. + */ + WindowState[] mPendingRemoveTmp = new WindowState[20]; + + // TODO: use WindowProcessController once go/wm-unified is done. + /** Mapping of process pids to configurations */ + final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>(); + + /** * Mapping of displayId to {@link DisplayImePolicy}. * Note that this can be accessed without holding the lock. */ @@ -683,6 +698,7 @@ public class WindowManagerService extends IWindowManager.Stub () -> mDisplayRotationController = null; final DisplayWindowListenerController mDisplayNotificationController; + final TaskSystemBarsListenerController mTaskSystemBarsListenerController; boolean mDisplayFrozen = false; long mDisplayFreezeTime = 0; @@ -1265,6 +1281,7 @@ public class WindowManagerService extends IWindowManager.Stub mScreenFrozenLock.setReferenceCounted(false); mDisplayNotificationController = new DisplayWindowListenerController(this); + mTaskSystemBarsListenerController = new TaskSystemBarsListenerController(); mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); @@ -2036,6 +2053,7 @@ public class WindowManagerService extends IWindowManager.Stub dc.mWinRemovedSinceNullFocus.add(win); } mEmbeddedWindowController.onWindowRemoved(win); + mPendingRemove.remove(win); mResizingWindows.remove(win); updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); mWindowsChanged = true; @@ -6364,6 +6382,23 @@ public class WindowManagerService extends IWindowManager.Stub } } } + if (mPendingRemove.size() > 0) { + pw.println(); + pw.println(" Remove pending for:"); + for (int i=mPendingRemove.size()-1; i>=0; i--) { + WindowState w = mPendingRemove.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Remove #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } if (mForceRemoves != null && mForceRemoves.size() > 0) { pw.println(); pw.println(" Windows force removing:"); @@ -7591,6 +7626,20 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void registerTaskSystemBarsListener(TaskSystemBarsListener listener) { + synchronized (mGlobalLock) { + mTaskSystemBarsListenerController.registerListener(listener); + } + } + + @Override + public void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener) { + synchronized (mGlobalLock) { + mTaskSystemBarsListenerController.unregisterListener(listener); + } + } + + @Override public void registerKeyguardExitAnimationStartListener( KeyguardExitAnimationStartListener listener) { synchronized (mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0a02b4442518..79c64b19882d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4887,20 +4887,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (hasSurface) { mWmService.mDestroySurface.add(this); } + if (mRemoveOnExit) { + mWmService.mPendingRemove.add(this); + mRemoveOnExit = false; + } } mAnimatingExit = false; getDisplayContent().mWallpaperController.hideWallpapers(this); } - @Override - boolean handleCompleteDeferredRemoval() { - if (mRemoveOnExit) { - mRemoveOnExit = false; - removeImmediately(); - } - return super.handleCompleteDeferredRemoval(); - } - boolean clearAnimatingFlags() { boolean didSomething = false; // We don't want to clear it out for windows that get replaced, because the diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 161d7ce350fc..166a0f5272cf 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -244,6 +244,9 @@ bool hasLatLong(const GnssLocation_V2_0& location) { return hasLatLong(location.v1_0); } +bool isSvStatusRegistered = false; +bool isNmeaRegistered = false; + } // namespace static inline jboolean boolToJbool(bool value) { @@ -505,6 +508,13 @@ uint32_t GnssCallback::getConstellationType( template <class T_list, class T_sv_info> Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) { + // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework. + if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() == 1) { + if (!isSvStatusRegistered) { + return Void(); + } + } + JNIEnv* env = getJniEnv(); uint32_t listSize = getGnssSvInfoListSize(svStatus); @@ -566,8 +576,12 @@ Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) { return Void(); } -Return<void> GnssCallback::gnssNmeaCb( - int64_t timestamp, const ::android::hardware::hidl_string& nmea) { +Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp, + const ::android::hardware::hidl_string& nmea) { + // In HIDL, if no listener is registered, do not report nmea to the framework. + if (!isNmeaRegistered) { + return Void(); + } JNIEnv* env = getJniEnv(); /* * The Java code will call back to read these values. @@ -680,6 +694,12 @@ Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) { } Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) { + // In AIDL v1, if no listener is registered, do not report nmea to the framework. + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() == 1) { + if (!isNmeaRegistered) { + return Status::ok(); + } + } JNIEnv* env = getJniEnv(); /* * The Java code will call back to read these values. @@ -1562,6 +1582,58 @@ static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jcl return checkHidlReturn(result, "IGnss stop() failed."); } +static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */, + jclass) { + isSvStatusRegistered = true; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->startSvStatus(); + return checkAidlStatus(status, "IGnssAidl startSvStatus() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */, + jclass) { + isSvStatusRegistered = false; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->stopSvStatus(); + return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection( + JNIEnv* /* env */, jclass) { + isNmeaRegistered = true; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->startNmea(); + return checkAidlStatus(status, "IGnssAidl startNmea() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */, + jclass) { + isNmeaRegistered = false; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->stopNmea(); + return checkAidlStatus(status, "IGnssAidl stopNmea() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass, jint flags) { if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { @@ -1989,7 +2061,7 @@ static SingleSatCorrection_V1_0 getSingleSatCorrection_1_0_withoutConstellation( jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc); uint16_t corrFlags = static_cast<uint16_t>(correctionFlags); - jobject reflectingPlaneObj; + jobject reflectingPlaneObj = nullptr; bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0; if (has_ref_plane) { reflectingPlaneObj = @@ -2013,6 +2085,7 @@ static SingleSatCorrection_V1_0 getSingleSatCorrection_1_0_withoutConstellation( .azimuthDegrees = azimuthDegreeRefPlane, }; } + env->DeleteLocalRef(reflectingPlaneObj); SingleSatCorrection_V1_0 singleSatCorrection = { .singleSatCorrectionFlags = corrFlags, @@ -2044,6 +2117,7 @@ static void getSingleSatCorrectionList_1_1(JNIEnv* env, jobject singleSatCorrect }; list[i] = singleSatCorrection_1_1; + env->DeleteLocalRef(singleSatCorrectionObj); } } @@ -2061,6 +2135,7 @@ static void getSingleSatCorrectionList_1_0(JNIEnv* env, jobject singleSatCorrect singleSatCorrection.constellation = static_cast<GnssConstellationType_V1_0>(constType), list[i] = singleSatCorrection; + env->DeleteLocalRef(singleSatCorrectionObj); } } @@ -2131,6 +2206,7 @@ static jboolean android_location_gnss_hal_GnssNative_inject_measurement_correcti hidl_vec<SingleSatCorrection_V1_0> list(len); getSingleSatCorrectionList_1_0(env, singleSatCorrectionList, list); + env->DeleteLocalRef(singleSatCorrectionList); measurementCorrections_1_0.satCorrections = list; auto result = gnssCorrectionsIface_V1_0->setCorrections(measurementCorrections_1_0); @@ -2365,6 +2441,16 @@ static const JNINativeMethod sLocationProviderMethods[] = { {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void*>( android_location_gnss_hal_GnssNative_is_gnss_visibility_control_supported)}, + {"native_start_sv_status_collection", "()Z", + reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_sv_status_collection)}, + {"native_stop_sv_status_collection", "()Z", + reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_sv_status_collection)}, + {"native_start_nmea_message_collection", "()Z", + reinterpret_cast<void*>( + android_location_gnss_hal_GnssNative_start_nmea_message_collection)}, + {"native_stop_nmea_message_collection", "()Z", + reinterpret_cast<void*>( + android_location_gnss_hal_GnssNative_stop_nmea_message_collection)}, }; static const JNINativeMethod sBatchingMethods[] = { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ce937d2c1353..e34178ab9cd2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -18515,9 +18515,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) - || isSystemUid(caller), + || canQueryAdminPolicy(caller), "SSID allowlist can only be retrieved by a device owner or " - + "a profile owner on an organization-owned device or a system app."); + + "a profile owner on an organization-owned device or " + + "an app with the QUERY_ADMIN_POLICY permission."); synchronized (getLockObject()) { final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( UserHandle.USER_SYSTEM); @@ -18553,9 +18554,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) - || isSystemUid(caller), + || canQueryAdminPolicy(caller), "SSID denylist can only be retrieved by a device owner or " - + "a profile owner on an organization-owned device or a system app."); + + "a profile owner on an organization-owned device or " + + "an app with the QUERY_ADMIN_POLICY permission."); synchronized (getLockObject()) { final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( UserHandle.USER_SYSTEM); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c9aeabd17191..d0c861fa912e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -366,6 +366,8 @@ public final class SystemServer implements Dumpable { "com.android.server.adb.AdbService$Lifecycle"; private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.speech.SpeechRecognitionManagerService"; + private static final String WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS = + "com.android.server.wallpapereffectsgeneration.WallpaperEffectsGenerationManagerService"; private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS = "com.android.server.appprediction.AppPredictionManagerService"; private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS = @@ -1895,7 +1897,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartIpSecService"); try { ipSecService = IpSecService.create(context); @@ -2126,6 +2127,14 @@ public final class SystemServer implements Dumpable { Slog.i(TAG, "Wallpaper service disabled by config"); } + // WallpaperEffectsGeneration manager service + // TODO (b/135218095): Use deviceHasConfigString(context, + // R.string.config_defaultWallpaperEffectsGenerationService) + t.traceBegin("StartWallpaperEffectsGenerationService"); + mSystemServiceManager.startService( + WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + t.traceBegin("StartAudioService"); if (!isArc) { mSystemServiceManager.startService(AudioService.Lifecycle.class); diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 9e221bec4749..75669d50c75d 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -52,6 +52,7 @@ android_test { "service-blobstore", "service-jobscheduler", "service-permission.impl", + "service-supplementalprocess.impl", "services.core", "services.devicepolicy", "services.net", diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml new file mode 100644 index 000000000000..eb154518c911 --- /dev/null +++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<game-mode-config + xmlns:android="http://schemas.android.com/apk/res/android" + android:supportsPerformanceGameMode="false" + android:supportsBatteryGameMode="false" + android:allowGameAngleDriver="false" + android:allowGameDownscaling="false" + android:allowGameFpsOverride="false" +/>
\ No newline at end of file diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml new file mode 100644 index 000000000000..65b7467b80f5 --- /dev/null +++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<game-mode-config + xmlns:android="http://schemas.android.com/apk/res/android" + android:supportsPerformanceGameMode="false" + android:supportsBatteryGameMode="false" + android:allowGameAngleDriver="true" + android:allowGameDownscaling="true" + android:allowGameFpsOverride="true" +/>
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java index fec9b1249d17..e89c812ba1fb 100644 --- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java +++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java @@ -359,6 +359,34 @@ public final class GameSessionTest { LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder(); } + @Test + public void dispatchTransientVisibilityChanged_valueUnchanged_doesNotInvokeCallback() { + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false); + + assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures).hasSize(0); + } + + @Test + public void dispatchTransientVisibilityChanged_valueChanged_invokesCallback() { + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true); + + assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures) + .containsExactly(true).inOrder(); + } + + @Test + public void dispatchTransientVisibilityChanged_manyTimes_invokesCallbackWhenValueChanges() { + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false); + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true); + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false); + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false); + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true); + mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true); + + assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures) + .containsExactly(true, false, true).inOrder(); + } + private static class LifecycleTrackingGameSession extends GameSession { private enum LifecycleMethodCall { ON_CREATE, @@ -368,6 +396,8 @@ public final class GameSessionTest { } final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>(); + final List<Boolean> mCapturedTransientSystemBarVisibilityFromRevealGestures = + new ArrayList<>(); @Override public void onCreate() { @@ -387,5 +417,11 @@ public final class GameSessionTest { mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED); } } + + @Override + public void onTransientSystemBarVisibilityFromRevealGestureChanged( + boolean visibleDueToGesture) { + mCapturedTransientSystemBarVisibilityFromRevealGestures.add(visibleDueToGesture); + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index eed2d4251cd7..d2358a08624d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -23,20 +23,32 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; import android.app.GameManager; import android.app.GameModeInfo; +import android.app.GameState; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.hardware.power.Mode; import android.os.Bundle; +import android.os.PowerManagerInternal; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; @@ -46,6 +58,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.LocalServices; import com.android.server.SystemService; import org.junit.After; @@ -76,6 +89,8 @@ public class GameManagerServiceTests { private TestLooper mTestLooper; @Mock private PackageManager mMockPackageManager; + @Mock + private PowerManagerInternal mMockPowerManager; // Stolen from ConnectivityServiceTest.MockContext class MockContext extends ContextWrapper { @@ -149,19 +164,27 @@ public class GameManagerServiceTests { mPackageName = mMockContext.getPackageName(); final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.category = ApplicationInfo.CATEGORY_GAME; + applicationInfo.packageName = mPackageName; final PackageInfo pi = new PackageInfo(); pi.packageName = mPackageName; pi.applicationInfo = applicationInfo; final List<PackageInfo> packages = new ArrayList<>(); packages.add(pi); + + final Resources resources = + InstrumentationRegistry.getInstrumentation().getContext().getResources(); + when(mMockPackageManager.getResourcesForApplication(anyString())) + .thenReturn(resources); when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt())) .thenReturn(packages); when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); + LocalServices.addService(PowerManagerInternal.class, mMockPowerManager); } @After public void tearDown() throws Exception { + LocalServices.removeServiceForTest(PowerManagerInternal.class); GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.disableCompatScale(mPackageName); if (mMockingSession != null) { @@ -310,6 +333,46 @@ public class GameManagerServiceTests { .thenReturn(applicationInfo); } + private void mockInterventionsEnabledFromXml() throws Exception { + final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( + mPackageName, PackageManager.GET_META_DATA, USER_ID_1); + Bundle metaDataBundle = new Bundle(); + final int resId = 123; + metaDataBundle.putInt( + GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId); + applicationInfo.metaData = metaDataBundle; + when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(applicationInfo); + seedGameManagerServiceMetaDataFromFile(mPackageName, resId, + "res/xml/gama_manager_service_metadata_config_enabled.xml"); + } + + private void mockInterventionsDisabledFromXml() throws Exception { + final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser( + mPackageName, PackageManager.GET_META_DATA, USER_ID_1); + Bundle metaDataBundle = new Bundle(); + final int resId = 123; + metaDataBundle.putInt( + GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId); + applicationInfo.metaData = metaDataBundle; + when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(applicationInfo); + seedGameManagerServiceMetaDataFromFile(mPackageName, resId, + "res/xml/gama_manager_service_metadata_config_disabled.xml"); + } + + + private void seedGameManagerServiceMetaDataFromFile(String packageName, int resId, + String fileName) + throws Exception { + AssetManager assetManager = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + XmlResourceParser xmlResourceParser = + assetManager.openXmlResourceParser(fileName); + when(mMockPackageManager.getXml(eq(packageName), eq(resId), any())) + .thenReturn(xmlResourceParser); + } + /** * By default game mode is not supported. */ @@ -499,8 +562,8 @@ public class GameManagerServiceTests { gameManagerService.getConfig(mPackageName); assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled); - // Validate GameManagerService.getAngleEnabled() returns the correct value. - assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled); + // Validate GameManagerService.isAngleEnabled() returns the correct value. + assertEquals(gameManagerService.isAngleEnabled(mPackageName, USER_ID_1), angleEnabled); } private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) { @@ -511,7 +574,7 @@ public class GameManagerServiceTests { } GameManagerService.GamePackageConfiguration config = gameManagerService.getConfig(mPackageName); - assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps); + assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps()); } /** @@ -893,6 +956,36 @@ public class GameManagerServiceTests { } @Test + public void testGameModeConfigAllowFpsTrue() throws Exception { + mockDeviceConfigAll(); + mockModifyGameModeGranted(); + mockInterventionsEnabledFromXml(); + GameManagerService gameManagerService = new GameManagerService(mMockContext, + mTestLooper.getLooper()); + startUser(gameManagerService, USER_ID_1); + GameManagerService.GamePackageConfiguration config = + gameManagerService.getConfig(mPackageName); + assertEquals(90, + config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps()); + assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps()); + } + + @Test + public void testGameModeConfigAllowFpsFalse() throws Exception { + mockDeviceConfigAll(); + mockModifyGameModeGranted(); + mockInterventionsDisabledFromXml(); + GameManagerService gameManagerService = new GameManagerService(mMockContext, + mTestLooper.getLooper()); + startUser(gameManagerService, USER_ID_1); + GameManagerService.GamePackageConfiguration config = + gameManagerService.getConfig(mPackageName); + assertEquals(0, + config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps()); + assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps()); + } + + @Test public void testInterventionFps() throws Exception { mockDeviceConfigAll(); mockModifyGameModeGranted(); @@ -1052,4 +1145,41 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode()); assertEquals(0, gameModeInfo.getAvailableGameModes().length); } + + @Test + public void testGameStateLoadingRequiresPerformanceMode() { + mockDeviceConfigNone(); + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + startUser(gameManagerService, USER_ID_1); + GameState gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean()); + } + + private void setGameState(boolean isLoading) { + mockDeviceConfigNone(); + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + startUser(gameManagerService, USER_ID_1); + gameManagerService.setGameMode( + mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); + GameState gameState = new GameState(isLoading, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading); + } + + @Test + public void testSetGameStateLoading() { + setGameState(true); + } + + @Test + public void testSetGameStateNotLoading() { + setGameState(false); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 317a51b3fe2d..08de62bc6537 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -31,7 +31,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import android.Manifest; @@ -72,6 +71,7 @@ import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.FunctionalUtils.ThrowingConsumer; import com.android.internal.util.Preconditions; import com.android.server.wm.WindowManagerInternal; +import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerService; import org.junit.After; @@ -112,6 +112,7 @@ public final class GameServiceProviderInstanceImplTest { private static final ComponentName GAME_B_MAIN_ACTIVITY = new ComponentName(GAME_B_PACKAGE, "com.package.game.b.MainActivity"); + private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888); private MockitoSession mMockingSession; @@ -131,6 +132,7 @@ public final class GameServiceProviderInstanceImplTest { private FakeGameSessionService mFakeGameSessionService; private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector; private ArrayList<ITaskStackListener> mTaskStackListeners; + private ArrayList<TaskSystemBarsListener> mTaskSystemBarsListeners; private ArrayList<RunningTaskInfo> mRunningTaskInfos; @Mock @@ -159,15 +161,25 @@ public final class GameServiceProviderInstanceImplTest { mTaskStackListeners.add(invocation.getArgument(0)); return null; }).when(mMockActivityTaskManager).registerTaskStackListener(any()); + doAnswer(invocation -> { + mTaskStackListeners.remove(invocation.getArgument(0)); + return null; + }).when(mMockActivityTaskManager).unregisterTaskStackListener(any()); + + mTaskSystemBarsListeners = new ArrayList<>(); + doAnswer(invocation -> { + mTaskSystemBarsListeners.add(invocation.getArgument(0)); + return null; + }).when(mMockWindowManagerInternal).registerTaskSystemBarsListener(any()); + doAnswer(invocation -> { + mTaskSystemBarsListeners.remove(invocation.getArgument(0)); + return null; + }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any()); mRunningTaskInfos = new ArrayList<>(); when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn( mRunningTaskInfos); - doAnswer(invocation -> { - mTaskStackListeners.remove(invocation.getArgument(0)); - return null; - }).when(mMockActivityTaskManager).unregisterTaskStackListener(any()); mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( new UserHandle(USER_ID), @@ -394,7 +406,60 @@ public final class GameServiceProviderInstanceImplTest { .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10)); - verifyNoMoreInteractions(mMockWindowManagerInternal); + } + + @Test + public void taskSystemBarsListenerChanged_noAssociatedGameSession_doesNothing() { + mGameServiceProviderInstance.start(); + + dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { + taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( + 10, + /* areVisible= */ false, + /* wereRevealedFromSwipeOnSystemBar= */ false); + }); + } + + @Test + public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() { + mGameServiceProviderInstance.start(); + startTask(10, GAME_A_MAIN_ACTIVITY); + mFakeGameService.requestCreateGameSession(10); + + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); + + dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { + taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( + 10, + /* areVisible= */ true, + /* wereRevealedFromSwipeOnSystemBar= */ true); + }); + + assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isTrue(); + } + + @Test + public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() { + mGameServiceProviderInstance.start(); + startTask(10, GAME_A_MAIN_ACTIVITY); + mFakeGameService.requestCreateGameSession(10); + + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); + + dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { + taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( + 10, + /* areVisible= */ true, + /* wereRevealedFromSwipeOnSystemBar= */ false); + }); + + assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isFalse(); } @Test @@ -492,7 +557,6 @@ public final class GameServiceProviderInstanceImplTest { verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10)); verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10)); - verifyNoMoreInteractions(mMockWindowManagerInternal); } @Test @@ -830,6 +894,13 @@ public final class GameServiceProviderInstanceImplTest { mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED); } + private void dispatchTaskSystemBarsEvent( + ThrowingConsumer<TaskSystemBarsListener> taskSystemBarsListenerConsumer) { + for (TaskSystemBarsListener listener : mTaskSystemBarsListeners) { + taskSystemBarsListenerConsumer.accept(listener); + } + } + static final class FakeGameService extends IGameService.Stub { private IGameServiceController mGameServiceController; @@ -944,6 +1015,7 @@ public final class GameServiceProviderInstanceImplTest { private static class FakeGameSession extends IGameSession.Stub { boolean mIsDestroyed = false; boolean mIsFocused = false; + boolean mAreTransientSystemBarsVisibleFromRevealGesture = false; @Override public void onDestroyed() { @@ -954,6 +1026,11 @@ public final class GameServiceProviderInstanceImplTest { public void onTaskFocusChanged(boolean focused) { mIsFocused = focused; } + + @Override + public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean areVisible) { + mAreTransientSystemBarsVisibleFromRevealGesture = areVisible; + } } private final class MockContext extends ContextWrapper { @@ -1005,5 +1082,4 @@ public final class GameServiceProviderInstanceImplTest { return mLastStartedIntent; } } - } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 6ae003163e4b..4ec1641b7157 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -69,6 +69,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.parsing.ParsingPackage import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.verify.domain.DomainVerificationManagerInternal +import com.android.server.supplementalprocess.SupplementalProcessManagerLocal import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.nullable @@ -335,6 +336,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { stageServicesExtensionScan() stageSystemSharedLibraryScan() stagePermissionsControllerScan() + stageSupplementalProcessScan() stageInstantAppResolverScan() } @@ -569,6 +571,22 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { } @Throws(Exception::class) + private fun stageSupplementalProcessScan() { + stageScanNewPackage("com.android.supplemental.process", + 1L, systemPartitions[0].privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + mockQueryServices(SupplementalProcessManagerLocal.SERVICE_INTERFACE, + createBasicServiceInfo( + pkg, applicationInfo, "SupplementalProcessService")) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) private fun stageSystemSharedLibraryScan() { stageScanNewPackage("android.ext.shared", 1L, systemPartitions[0].appFolder, diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java index 9ee1205b4325..3890d4d006e2 100644 --- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -24,14 +24,20 @@ import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFT import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.fail; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.attention.AttentionManagerInternal.ProximityCallbackInternal; import android.content.ComponentName; import android.content.Context; import android.os.IBinder; @@ -42,6 +48,7 @@ import android.os.RemoteException; import android.provider.DeviceConfig; import android.service.attention.IAttentionCallback; import android.service.attention.IAttentionService; +import android.service.attention.IProximityCallback; import androidx.test.filters.SmallTest; @@ -49,6 +56,7 @@ import com.android.server.attention.AttentionManagerService.AttentionCheck; import com.android.server.attention.AttentionManagerService.AttentionCheckCache; import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer; import com.android.server.attention.AttentionManagerService.AttentionHandler; +import com.android.server.attention.AttentionManagerService.ProximityUpdate; import org.junit.Before; import org.junit.Test; @@ -59,10 +67,13 @@ import org.mockito.MockitoAnnotations; /** * Tests for {@link com.android.server.attention.AttentionManagerService} */ +@SuppressWarnings("GuardedBy") @SmallTest public class AttentionManagerServiceTest { + private static final double PROXIMITY_SUCCESS_STATE = 1.0; private AttentionManagerService mSpyAttentionManager; private final int mTimeout = 1000; + private final Object mLock = new Object(); @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; @Mock @@ -73,6 +84,8 @@ public class AttentionManagerServiceTest { private IThermalService mMockIThermalService; @Mock Context mContext; + @Mock + private ProximityCallbackInternal mMockProximityCallbackInternal; @Before public void setUp() throws RemoteException { @@ -84,7 +97,6 @@ public class AttentionManagerServiceTest { doReturn(true).when(mMockIPowerManager).isInteractive(); mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null); - Object mLock = new Object(); // setup a spy on attention manager AttentionManagerService attentionManager = new AttentionManagerService( mContext, @@ -100,6 +112,119 @@ public class AttentionManagerServiceTest { mSpyAttentionManager); mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck; mSpyAttentionManager.mService = new MockIAttentionService(); + doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); + } + + @Test + public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() { + mSpyAttentionManager.mIsServiceEnabled = false; + + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isFalse(); + } + + @Test + public void testRegisterProximityUpdates_returnFalseWhenServiceUnavailable() { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(false).when(mSpyAttentionManager).isServiceAvailable(); + + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isFalse(); + } + + @Test + public void testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract() + throws RemoteException { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(false).when(mMockIPowerManager).isInteractive(); + + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isFalse(); + } + + @Test + public void testRegisterProximityUpdates_callOnSuccess() throws RemoteException { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isTrue(); + verify(mMockProximityCallbackInternal, times(1)) + .onProximityUpdate(PROXIMITY_SUCCESS_STATE); + } + + @Test + public void testRegisterProximityUpdates_callOnSuccessTwiceInARow() throws RemoteException { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isTrue(); + + ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate; + assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal)) + .isTrue(); + assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate); + verify(mMockProximityCallbackInternal, times(1)) + .onProximityUpdate(PROXIMITY_SUCCESS_STATE); + } + + @Test + public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() { + mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal); + verifyZeroInteractions(mMockProximityCallbackInternal); + } + + @Test + public void testUnregisterProximityUpdates_noCrashWhenCallbackMismatched() + throws RemoteException { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal); + verify(mMockProximityCallbackInternal, times(1)) + .onProximityUpdate(PROXIMITY_SUCCESS_STATE); + + ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() { + @Override + public void onProximityUpdate(double distance) { + fail("Callback shouldn't have responded."); + } + }; + mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback); + + verifyNoMoreInteractions(mMockProximityCallbackInternal); + } + + @Test + public void testUnregisterProximityUpdates_cancelRegistrationWhenMatched() + throws RemoteException { + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal); + mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal); + + assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull(); + } + + @Test + public void testUnregisterProximityUpdates_noCrashWhenTwiceInARow() throws RemoteException { + // Attention Service registers proximity updates. + mSpyAttentionManager.mIsServiceEnabled = true; + doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal); + verify(mMockProximityCallbackInternal, times(1)) + .onProximityUpdate(PROXIMITY_SUCCESS_STATE); + + // Attention Service unregisters the proximity update twice in a row. + mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal); + mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal); + verifyNoMoreInteractions(mMockProximityCallbackInternal); } @Test @@ -127,7 +252,6 @@ public class AttentionManagerServiceTest { mSpyAttentionManager.mIsServiceEnabled = true; doReturn(true).when(mSpyAttentionManager).isServiceAvailable(); doReturn(true).when(mMockIPowerManager).isInteractive(); - doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); mSpyAttentionManager.mCurrentAttentionCheck = null; AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); @@ -213,6 +337,13 @@ public class AttentionManagerServiceTest { public void cancelAttentionCheck(IAttentionCallback callback) { } + public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException { + callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE); + } + + public void onStopProximityUpdates() throws RemoteException { + } + public IBinder asBinder() { return null; } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 4caa85cee7db..f5a56891b5f5 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -81,6 +81,7 @@ public class AutomaticBrightnessControllerTest { @Mock HysteresisLevels mScreenBrightnessThresholds; @Mock Handler mNoOpHandler; @Mock HighBrightnessModeController mHbmController; + @Mock BrightnessThrottler mBrightnessThrottler; @Before public void setUp() { @@ -128,12 +129,15 @@ public class AutomaticBrightnessControllerTest { INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds, mScreenBrightnessThresholds, - mContext, mHbmController, mIdleBrightnessMappingStrategy, + mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG ); when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT); when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT); + // Disable brightness throttling by default. Individual tests can enable it as needed. + when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); + when(mBrightnessThrottler.isThrottled()).thenReturn(false); // Configure the brightness controller and grab an instance of the sensor listener, // through which we can deliver fake (for test) sensor values. @@ -420,4 +424,47 @@ public class AutomaticBrightnessControllerTest { assertEquals(600f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON); assertEquals(250f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON); } + + @Test + public void testBrightnessGetsThrottled() throws Exception { + Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); + mController = setupController(lightSensor); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Set up system to return max brightness at 100 lux + final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT; + final float lux = 100.0f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)) + .thenReturn(lux); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)) + .thenReturn(lux); + when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt())) + .thenReturn(normalizedBrightness); + + // Sensor reads 100 lux. We should get max brightness. + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux)); + assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f); + + // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState()) + final float throttledBrightness = 0.123f; + when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness); + when(mBrightnessThrottler.isThrottled()).thenReturn(true); + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, + BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */, + 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); + assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f); + + // Remove throttling and notify ABC again + when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); + when(mBrightnessThrottler.isThrottled()).thenReturn(false); + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */, + BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */, + 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); + assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java index d441143272d9..0028969d85a0 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java @@ -165,6 +165,17 @@ final class FakeHdmiCecConfig extends HdmiCecConfig { R.bool.config_cecTvSendStandbyOnSleepDisabled_default); doReturn(true).when(resources).getBoolean( + R.bool.config_cecSetMenuLanguage_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSetMenuLanguageEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSetMenuLanguageEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSetMenuLanguageDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecSetMenuLanguageDisabled_default); + + doReturn(true).when(resources).getBoolean( R.bool.config_cecRcProfileTv_userConfigurable); doReturn(true).when(resources).getBoolean( R.bool.config_cecRcProfileTvNone_allowed); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index 85d30a6dbc36..ae7b494fffbb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -83,6 +83,7 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, @@ -121,6 +122,7 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, @@ -159,6 +161,7 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, 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 7751ef564138..c48a974ddbb2 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -42,6 +42,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; import android.hardware.hdmi.IHdmiControlStatusChangeListener; +import android.hardware.hdmi.IHdmiVendorCommandListener; import android.os.Binder; import android.os.Looper; import android.os.RemoteException; @@ -747,6 +748,114 @@ public class HdmiControlServiceTest { } @Test + public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandNoId = + HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params); + mNativeWrapper.onCecMessage(vendorCommandNoId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue(); + assertThat(vendorCmdListener.mParamsCorrect).isTrue(); + assertThat(vendorCmdListener.mHasVendorId).isFalse(); + } + + @Test + public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandWithId = + HdmiCecMessageBuilder.buildVendorCommandWithId( + sourceAddress, destAddress, vendorId, params); + mNativeWrapper.onCecMessage(vendorCommandWithId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue(); + assertThat(vendorCmdListener.mParamsCorrect).isTrue(); + assertThat(vendorCmdListener.mHasVendorId).isTrue(); + } + + @Test + public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + int diffVendorId = 0x345678; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandWithDiffId = + HdmiCecMessageBuilder.buildVendorCommandWithId( + sourceAddress, destAddress, diffVendorId, params); + mNativeWrapper.onCecMessage(vendorCommandWithDiffId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse(); + } + + private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub { + boolean mVendorCommandCallbackReceived = false; + boolean mParamsCorrect = false; + boolean mHasVendorId = false; + + int mSourceAddress; + int mDestAddress; + byte[] mParams; + int mVendorId; + + VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) { + this.mSourceAddress = sourceAddress; + this.mDestAddress = destAddress; + this.mParams = params.clone(); + this.mVendorId = vendorId; + } + + @Override + public void onReceived( + int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) { + mVendorCommandCallbackReceived = true; + if (mSourceAddress == sourceAddress && mDestAddress == destAddress) { + byte[] expectedParams; + if (hasVendorId) { + // If the command has vendor ID, we have to add it to mParams. + expectedParams = new byte[params.length]; + expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF); + expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF); + expectedParams[2] = (byte) (mVendorId & 0xFF); + System.arraycopy(mParams, 0, expectedParams, 3, mParams.length); + } else { + expectedParams = params.clone(); + } + if (Arrays.equals(expectedParams, params)) { + mParamsCorrect = true; + } + } + mHasVendorId = hasVendorId; + } + + @Override + public void onControlStateChanged(boolean enabled, int reason) {} + } + + @Test public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() { HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby( Constants.ADDR_TV, diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java index e65501329cdf..27e8d69aa762 100644 --- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java +++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java @@ -16,6 +16,8 @@ package com.android.server.usage; +import static android.app.ActivityManager.procStateToString; + import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS; import android.annotation.ElapsedRealtimeLong; @@ -23,6 +25,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager.ProcessState; import android.app.usage.BroadcastResponseStats; import android.os.UserHandle; import android.text.TextUtils; @@ -78,12 +81,17 @@ class BroadcastResponseStatsTracker { // TODO (206518114): Move all callbacks handling to a handler thread. void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, - @ElapsedRealtimeLong long timestampMs) { + @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) { if (DEBUG_RESPONSE_STATS) { - Slog.d(TAG, TextUtils.formatSimple( - "reportBroadcastDispatchEvent; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s", + Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; " + + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s", sourceUid, targetPackage, targetUser, idForResponseEvent, - TimeUtils.formatDuration(timestampMs))); + TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState))); + } + if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) { + // No need to track the broadcast response state while the target app is + // in the foreground. + return; } synchronized (mLock) { final LongSparseArray<BroadcastEvent> broadcastEvents = diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 6906f20f26c2..98a41bcf5adf 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -42,6 +42,7 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessState; import android.app.AppOpsManager; import android.app.IUidObserver; import android.app.PendingIntent; @@ -3042,9 +3043,9 @@ public class UsageStatsService extends SystemService implements @Override public void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage, @NonNull UserHandle targetUser, long idForResponseEvent, - @ElapsedRealtimeLong long timestampMs) { + @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) { mResponseStatsTracker.reportBroadcastDispatchEvent(sourceUid, targetPackage, - targetUser, idForResponseEvent, timestampMs); + targetUser, idForResponseEvent, timestampMs, targetUidProcState); } @Override diff --git a/services/wallpapereffectsgeneration/Android.bp b/services/wallpapereffectsgeneration/Android.bp new file mode 100644 index 000000000000..4dbb0fd82db1 --- /dev/null +++ b/services/wallpapereffectsgeneration/Android.bp @@ -0,0 +1,22 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "services.wallpapereffectsgeneration-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.wallpapereffectsgeneration", + defaults: ["platform_service_defaults"], + srcs: [":services.wallpapereffectsgeneration-sources"], + libs: ["services.core"], +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java new file mode 100644 index 000000000000..c228dafcee8e --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wallpapereffectsgeneration; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.service.wallpapereffectsgeneration.IWallpaperEffectsGenerationService; +import android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService; +import android.text.format.DateUtils; + +import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; + + +/** + * Proxy to the + * {@link android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService} + * implementation in another process. + */ +public class RemoteWallpaperEffectsGenerationService extends + AbstractMultiplePendingRequestsRemoteService<RemoteWallpaperEffectsGenerationService, + IWallpaperEffectsGenerationService> { + + private static final String TAG = + RemoteWallpaperEffectsGenerationService.class.getSimpleName(); + + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; + + private final RemoteWallpaperEffectsGenerationServiceCallback mCallback; + + public RemoteWallpaperEffectsGenerationService(Context context, + ComponentName componentName, int userId, + RemoteWallpaperEffectsGenerationServiceCallback callback, + boolean bindInstantServiceAllowed, + boolean verbose) { + super(context, WallpaperEffectsGenerationService.SERVICE_INTERFACE, + componentName, userId, callback, + context.getMainThreadHandler(), + bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, + verbose, /* initialCapacity= */ 1); + mCallback = callback; + } + + @Override + protected IWallpaperEffectsGenerationService getServiceInterface(IBinder service) { + return IWallpaperEffectsGenerationService.Stub.asInterface(service); + } + + @Override + protected long getTimeoutIdleBindMillis() { + return PERMANENT_BOUND_TIMEOUT_MS; + } + + @Override + protected long getRemoteRequestMillis() { + return TIMEOUT_REMOTE_REQUEST_MILLIS; + } + + /** + * Schedules a request to bind to the remote service. + */ + public void reconnect() { + super.scheduleBind(); + } + + /** + * Schedule async request on remote service. + */ + public void scheduleOnResolvedService( + @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) { + scheduleAsyncRequest(request); + } + + /** + * Execute async request on remote service immediately instead of sending it to Handler queue. + */ + public void executeOnResolvedService( + @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) { + executeAsyncRequest(request); + } + + /** + * Notifies server (WallpaperEffectsGenerationPerUserService) about unexpected events.. + */ + public interface RemoteWallpaperEffectsGenerationServiceCallback + extends VultureCallback<RemoteWallpaperEffectsGenerationService> { + /** + * Notifies change in connected state of the remote service. + */ + void onConnectedStateChanged(boolean connected); + } + + @Override // from AbstractRemoteService + protected void handleOnConnectedStateChanged(boolean connected) { + if (mCallback != null) { + mCallback.onConnectedStateChanged(connected); + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java new file mode 100644 index 000000000000..0d0b3e098750 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpapereffectsgeneration; + +import static android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION; +import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; +import static android.content.Context.WALLPAPER_EFFECTS_GENERATION_SERVICE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; +import android.app.wallpapereffectsgeneration.CinematicEffectRequest; +import android.app.wallpapereffectsgeneration.CinematicEffectResponse; +import android.app.wallpapereffectsgeneration.ICinematicEffectListener; +import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager; +import android.content.Context; +import android.os.Binder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.util.Slog; + +import com.android.server.LocalServices; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; +import com.android.server.wm.ActivityTaskManagerInternal; + +import java.io.FileDescriptor; +import java.util.function.Consumer; + +/** + * A service used to return wallpaper effect given a request. + */ +public class WallpaperEffectsGenerationManagerService extends + AbstractMasterSystemService<WallpaperEffectsGenerationManagerService, + WallpaperEffectsGenerationPerUserService> { + private static final String TAG = + WallpaperEffectsGenerationManagerService.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + private final ActivityTaskManagerInternal mActivityTaskManagerInternal; + + public WallpaperEffectsGenerationManagerService(Context context) { + super(context, + new FrameworkResourcesServiceNameResolver(context, + com.android.internal.R.string.config_defaultWallpaperEffectsGenerationService), + null, + PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH); + mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + } + + @Override + protected WallpaperEffectsGenerationPerUserService newServiceLocked(int resolvedUserId, + boolean disabled) { + return new WallpaperEffectsGenerationPerUserService(this, mLock, resolvedUserId); + } + + @Override + public void onStart() { + publishBinderService(WALLPAPER_EFFECTS_GENERATION_SERVICE, + new WallpaperEffectsGenerationManagerStub()); + } + + @Override + protected void enforceCallingPermissionForManagement() { + getContext().enforceCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION, TAG); + } + + @Override // from AbstractMasterSystemService + protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { + final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + service.onPackageUpdatedLocked(); + } + } + + @Override // from AbstractMasterSystemService + protected void onServicePackageRestartedLocked(@UserIdInt int userId) { + final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + service.onPackageRestartedLocked(); + } + } + + @Override + protected int getMaximumTemporaryServiceDurationMs() { + return MAX_TEMP_SERVICE_DURATION_MS; + } + + private class WallpaperEffectsGenerationManagerStub + extends IWallpaperEffectsGenerationManager.Stub { + @Override + public void generateCinematicEffect(@NonNull CinematicEffectRequest request, + @NonNull ICinematicEffectListener listener) { + if (!runForUserLocked("generateCinematicEffect", (service) -> + service.onGenerateCinematicEffectLocked(request, listener))) { + try { + listener.onCinematicEffectGenerated( + new CinematicEffectResponse.Builder( + CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR, + request.getTaskId()).build()); + } catch (RemoteException e) { + if (DEBUG) { + Slog.d(TAG, "fail to invoke cinematic effect listener for task[" + + request.getTaskId() + "]"); + } + } + } + } + + @Override + public void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) { + runForUserLocked("returnCinematicResponse", (service) -> + service.onReturnCinematicEffectResponseLocked(response)); + } + + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, + @Nullable FileDescriptor err, + @NonNull String[] args, @Nullable ShellCallback callback, + @NonNull ResultReceiver resultReceiver) { + new WallpaperEffectsGenerationManagerServiceShellCommand( + WallpaperEffectsGenerationManagerService.this) + .exec(this, in, out, err, args, callback, resultReceiver); + } + + /** + * Execute the operation for the user locked. Return true if + * WallpaperEffectsGenerationPerUserService is found for the user. + * Otherwise return false. + */ + private boolean runForUserLocked(@NonNull final String func, + @NonNull final Consumer<WallpaperEffectsGenerationPerUserService> c) { + ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); + final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL, + null, null); + if (DEBUG) { + Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + } + Context ctx = getContext(); + if (!(ctx.checkCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION) + == PERMISSION_GRANTED + || mServiceNameResolver.isTemporary(userId) + || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) { + String msg = "Permission Denial: Cannot call " + func + " from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + final long origId = Binder.clearCallingIdentity(); + boolean accepted = false; + try { + synchronized (mLock) { + final WallpaperEffectsGenerationPerUserService service = + getServiceForUserLocked(userId); + if (service != null) { + accepted = true; + c.accept(service); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + return accepted; + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java new file mode 100644 index 000000000000..fc6f75fc254e --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpapereffectsgeneration; + +import android.annotation.NonNull; +import android.os.ShellCommand; + +import java.io.PrintWriter; + +/** + * The shell command implementation for the WallpaperEffectsGenerationService. + */ +public class WallpaperEffectsGenerationManagerServiceShellCommand extends ShellCommand { + + private static final String TAG = + WallpaperEffectsGenerationManagerServiceShellCommand.class.getSimpleName(); + + private final WallpaperEffectsGenerationManagerService mService; + + public WallpaperEffectsGenerationManagerServiceShellCommand( + @NonNull WallpaperEffectsGenerationManagerService service) { + mService = service; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter pw = getOutPrintWriter(); + switch (cmd) { + case "set": { + final String what = getNextArgRequired(); + switch (what) { + case "temporary-service": { + final int userId = Integer.parseInt(getNextArgRequired()); + String serviceName = getNextArg(); + if (serviceName == null) { + mService.resetTemporaryService(userId); + pw.println("WallpaperEffectsGenerationService temporarily reset. "); + return 0; + } + final int duration = Integer.parseInt(getNextArgRequired()); + mService.setTemporaryService(userId, serviceName, duration); + pw.println("WallpaperEffectsGenerationService temporarily set to " + + serviceName + " for " + duration + "ms"); + break; + } + } + } + break; + default: + return handleDefaultCommands(cmd); + } + return 0; + } + + @Override + public void onHelp() { + try (PrintWriter pw = getOutPrintWriter()) { + pw.println("WallpaperEffectsGenerationService commands:"); + pw.println(" help"); + pw.println(" Prints this help text."); + pw.println(""); + pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]"); + pw.println(" Temporarily (for DURATION ms) changes the service implemtation."); + pw.println(" To reset, call with just the USER_ID argument."); + pw.println(""); + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java new file mode 100644 index 000000000000..d541051e5ab2 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpapereffectsgeneration; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppGlobals; +import android.app.wallpapereffectsgeneration.CinematicEffectRequest; +import android.app.wallpapereffectsgeneration.CinematicEffectResponse; +import android.app.wallpapereffectsgeneration.ICinematicEffectListener; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.infra.AbstractPerUserSystemService; + +/** + * Per-user instance of {@link WallpaperEffectsGenerationManagerService}. + */ +public class WallpaperEffectsGenerationPerUserService extends + AbstractPerUserSystemService<WallpaperEffectsGenerationPerUserService, + WallpaperEffectsGenerationManagerService> implements + RemoteWallpaperEffectsGenerationService.RemoteWallpaperEffectsGenerationServiceCallback { + + private static final String TAG = + WallpaperEffectsGenerationPerUserService.class.getSimpleName(); + + @GuardedBy("mLock") + private CinematicEffectListenerWrapper mCinematicEffectListenerWrapper; + + @Nullable + @GuardedBy("mLock") + private RemoteWallpaperEffectsGenerationService mRemoteService; + + protected WallpaperEffectsGenerationPerUserService( + WallpaperEffectsGenerationManagerService master, + Object lock, int userId) { + super(master, lock, userId); + } + + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws NameNotFoundException { + ServiceInfo si; + try { + si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + PackageManager.GET_META_DATA, mUserId); + } catch (RemoteException e) { + throw new NameNotFoundException("Could not get service for " + serviceComponent); + } + if (!Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE.equals(si.permission)) { + Slog.w(TAG, "WallpaperEffectsGenerationService from '" + si.packageName + + "' does not require permission " + + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE); + throw new SecurityException("Service does not require permission " + + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE); + } + return si; + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected boolean updateLocked(boolean disabled) { + final boolean enabledChanged = super.updateLocked(disabled); + updateRemoteServiceLocked(); + return enabledChanged; + } + + /** + * Notifies the service of a new cinematic effect generation request. + */ + @GuardedBy("mLock") + public void onGenerateCinematicEffectLocked( + @NonNull CinematicEffectRequest cinematicEffectRequest, + @NonNull ICinematicEffectListener cinematicEffectListener) { + String newTaskId = cinematicEffectRequest.getTaskId(); + // Previous request is still being processed. + if (mCinematicEffectListenerWrapper != null) { + if (mCinematicEffectListenerWrapper.mTaskId.equals(newTaskId)) { + invokeCinematicListenerAndCleanup( + new CinematicEffectResponse.Builder( + CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId) + .build() + ); + } else { + invokeCinematicListenerAndCleanup( + new CinematicEffectResponse.Builder( + CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS, + newTaskId).build() + ); + } + return; + } + RemoteWallpaperEffectsGenerationService remoteService = ensureRemoteServiceLocked(); + if (remoteService != null) { + remoteService.executeOnResolvedService( + s -> s.onGenerateCinematicEffect(cinematicEffectRequest)); + mCinematicEffectListenerWrapper = + new CinematicEffectListenerWrapper(newTaskId, cinematicEffectListener); + } else { + if (isDebug()) { + Slog.d(TAG, "Remote service not found"); + } + try { + cinematicEffectListener.onCinematicEffectGenerated( + createErrorCinematicEffectResponse(newTaskId)); + } catch (RemoteException e) { + if (isDebug()) { + Slog.d(TAG, "Failed to invoke cinematic effect listener for task [" + newTaskId + + "]"); + } + } + } + } + + /** + * Notifies the service of a generated cinematic effect response. + */ + @GuardedBy("mLock") + public void onReturnCinematicEffectResponseLocked( + @NonNull CinematicEffectResponse cinematicEffectResponse) { + invokeCinematicListenerAndCleanup(cinematicEffectResponse); + } + + @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteService != null) { + mRemoteService.destroy(); + mRemoteService = null; + } + // End existing response and clean up listener for next request. + if (mCinematicEffectListenerWrapper != null) { + invokeCinematicListenerAndCleanup( + createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId)); + } + + } + + void onPackageUpdatedLocked() { + if (isDebug()) { + Slog.v(TAG, "onPackageUpdatedLocked()"); + } + destroyAndRebindRemoteService(); + } + + void onPackageRestartedLocked() { + if (isDebug()) { + Slog.v(TAG, "onPackageRestartedLocked()"); + } + destroyAndRebindRemoteService(); + } + + private void destroyAndRebindRemoteService() { + if (mRemoteService == null) { + return; + } + + if (isDebug()) { + Slog.d(TAG, "Destroying the old remote service."); + } + mRemoteService.destroy(); + mRemoteService = null; + mRemoteService = ensureRemoteServiceLocked(); + if (mRemoteService != null) { + if (isDebug()) { + Slog.d(TAG, "Rebinding to the new remote service."); + } + mRemoteService.reconnect(); + } + // Clean up listener for next request. + if (mCinematicEffectListenerWrapper != null) { + invokeCinematicListenerAndCleanup( + createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId)); + } + } + + private CinematicEffectResponse createErrorCinematicEffectResponse(String taskId) { + return new CinematicEffectResponse.Builder( + CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR, + taskId).build(); + } + + @GuardedBy("mLock") + private void invokeCinematicListenerAndCleanup( + CinematicEffectResponse cinematicEffectResponse) { + try { + if (mCinematicEffectListenerWrapper != null + && mCinematicEffectListenerWrapper.mListener != null) { + mCinematicEffectListenerWrapper.mListener.onCinematicEffectGenerated( + cinematicEffectResponse); + } else { + if (isDebug()) { + Slog.w(TAG, "Cinematic effect listener not found for task[" + + mCinematicEffectListenerWrapper.mTaskId + "]"); + } + } + } catch (RemoteException e) { + if (isDebug()) { + Slog.w(TAG, "Error invoking cinematic effect listener for task[" + + mCinematicEffectListenerWrapper.mTaskId + "]"); + } + } finally { + mCinematicEffectListenerWrapper = null; + } + } + + @GuardedBy("mLock") + @Nullable + private RemoteWallpaperEffectsGenerationService ensureRemoteServiceLocked() { + if (mRemoteService == null) { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): not set"); + } + return null; + } + ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + + mRemoteService = new RemoteWallpaperEffectsGenerationService(getContext(), + serviceComponent, mUserId, this, + mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + } + + return mRemoteService; + } + + @Override // from RemoteWallpaperEffectsGenerationService + public void onServiceDied(RemoteWallpaperEffectsGenerationService service) { + Slog.w(TAG, "remote wallpaper effects generation service died"); + updateRemoteServiceLocked(); + } + + @Override // from RemoteWallpaperEffectsGenerationService + public void onConnectedStateChanged(boolean connected) { + if (!connected) { + Slog.w(TAG, "remote wallpaper effects generation service disconnected"); + updateRemoteServiceLocked(); + } + } + + private static final class CinematicEffectListenerWrapper { + @NonNull + private final String mTaskId; + @NonNull + private final ICinematicEffectListener mListener; + + CinematicEffectListenerWrapper( + @NonNull final String taskId, + @NonNull final ICinematicEffectListener listener) { + mTaskId = taskId; + mListener = listener; + } + } +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e0e791321819..3c277b7de018 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4540,9 +4540,7 @@ public class CarrierConfigManager { * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the * subscription from a group instead of adding it to a group. * - * TODO: Expose in a future release. - * - * @hide + * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}. */ public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000"; @@ -4555,9 +4553,7 @@ public class CarrierConfigManager { * <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from * its current group. * - * TODO: unhide this key. - * - * @hide + * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}. */ public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string"; @@ -4605,17 +4601,15 @@ public class CarrierConfigManager { "data_switch_validation_min_gap_long"; /** - * A boolean property indicating whether this subscription should be managed as an opportunistic - * subscription. - * - * If true, then this subscription will be selected based on available coverage and will not be - * available for a user in settings menus for selecting macro network providers. If unset, - * defaults to “false”. - * - * TODO: unhide this key. - * - * @hide - */ + * A boolean property indicating whether this subscription should be managed as an opportunistic + * subscription. + * + * If true, then this subscription will be selected based on available coverage and will not be + * available for a user in settings menus for selecting macro network providers. If unset, + * defaults to “false”. + * + * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}. + */ public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool"; |