diff options
179 files changed, 6319 insertions, 785 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index c29f3ae9052c..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 @@ -1435,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; @@ -3130,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 @@ -31425,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>); @@ -31466,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); @@ -31526,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); @@ -41221,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"; @@ -41305,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"; @@ -41348,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 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 6cde547e6571..6e7bc765c157 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -78,9 +78,9 @@ package android.app { package android.app.admin { public class DevicePolicyManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void acknowledgeNewUserDisclaimer(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getLogoutUser(); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int logoutUser(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int logoutUser(); field public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = "android.app.action.SHOW_NEW_USER_DISCLAIMER"; } @@ -141,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 49a1d629c718..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"; @@ -2554,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 { @@ -2742,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"; @@ -3713,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 { @@ -10294,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"; @@ -10645,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 @@ -10652,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"; } @@ -10660,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 { @@ -11083,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); @@ -11512,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(); @@ -11522,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 @@ -11704,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 52a180b3c9e0..e2a78c6414d2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -278,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); } @@ -497,6 +498,7 @@ package android.app.admin { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps(); method public boolean isCurrentInputMethodSetByOwner(); method public boolean isFactoryResetProtectionPolicySupported(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged(); method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); method @NonNull public static String operationSafetyReasonToString(int); method @NonNull public static String operationToString(int); @@ -800,6 +802,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); 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/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/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/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a4227a4d3074..83265800c04f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3556,7 +3556,8 @@ public class DevicePolicyManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void acknowledgeNewUserDisclaimer() { if (mService != null) { @@ -3569,6 +3570,25 @@ public class DevicePolicyManager { } /** + * Checks whether the new managed user disclaimer was viewed by the current user. + * + * @hide + */ + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + @TestApi + public boolean isNewUserDisclaimerAcknowledged() { + if (mService != null) { + try { + return mService.isNewUserDisclaimerAcknowledged(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. @@ -10146,7 +10166,7 @@ public class DevicePolicyManager { * @hide */ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.CREATE_USERS}) + android.Manifest.permission.INTERACT_ACROSS_USERS}) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public @UserOperationResult int logoutUser() { // TODO(b/214336184): add CTS test diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 927ee0c20eaf..a7a51f8f6caa 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -272,6 +272,7 @@ interface IDevicePolicyManager { int getLogoutUserId(); List<UserHandle> getSecondaryUsers(in ComponentName who); void acknowledgeNewUserDisclaimer(); + boolean isNewUserDisclaimerAcknowledged(); void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName); int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent); 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/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 fba61cfd801e..8f6e1e00c3f4 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -119,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; @@ -139,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; @@ -309,9 +305,6 @@ public class TrustAgentService extends Service { * {@link #grantTrust(CharSequence, long, int)}. * * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE - * - * TODO(b/213631672): Add CTS tests - * @hide */ public void onUserRequestedUnlock() { } @@ -624,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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 66d5e88423e6..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). @@ -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 04e29890568a..afe0f1bf0001 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8875,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 047c04b3596c..d57f5ba8179b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3255,6 +3255,11 @@ <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" /> 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/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/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 59893817da6f..f83431b58c62 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -308,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" /> @@ -878,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/action_chip_background.xml b/packages/SystemUI/res/drawable/action_chip_background.xml index eeff39b7a8fd..745470f4c61a 100644 --- a/packages/SystemUI/res/drawable/action_chip_background.xml +++ b/packages/SystemUI/res/drawable/action_chip_background.xml @@ -17,11 +17,11 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:color="@color/screenshot_button_ripple"> + android:color="@color/overlay_button_ripple"> <item android:id="@android:id/background"> <shape android:shape="rectangle"> <solid android:color="?androidprv:attr/colorAccentSecondary"/> - <corners android:radius="@dimen/screenshot_button_corner_radius"/> + <corners android:radius="@dimen/overlay_button_corner_radius"/> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/drawable/action_chip_container_background.xml b/packages/SystemUI/res/drawable/action_chip_container_background.xml index 72767a12bcf5..36083f1f0408 100644 --- a/packages/SystemUI/res/drawable/action_chip_container_background.xml +++ b/packages/SystemUI/res/drawable/action_chip_container_background.xml @@ -19,5 +19,5 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> <solid android:color="?androidprv:attr/colorSurface"/> - <corners android:radius="@dimen/screenshot_action_container_corner_radius"/> + <corners android:radius="@dimen/overlay_action_container_corner_radius"/> </shape> 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/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml index dd818a068d61..d8f56324566d 100644 --- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml +++ b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml @@ -17,6 +17,6 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:angle="90" - android:startColor="@color/screenshot_background_protection_start" + android:startColor="@color/overlay_background_protection_start" android:endColor="#00000000"/> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/screenshot_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml index 3c39fe2ecb06..3c39fe2ecb06 100644 --- a/packages/SystemUI/res/drawable/screenshot_button_background.xml +++ b/packages/SystemUI/res/drawable/overlay_button_background.xml 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/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 7e31909613ee..4817d453ba0b 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -17,7 +17,7 @@ <com.android.systemui.clipboardoverlay.DraggableConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:theme="@style/Screenshot" + android:theme="@style/FloatingOverlay" android:alpha="0" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -28,7 +28,7 @@ android:layout_width="0dp" android:elevation="1dp" android:background="@drawable/action_chip_container_background" - android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal" + android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" app:layout_constraintBottom_toBottomOf="@+id/actions_container" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/actions_container" @@ -37,9 +37,9 @@ android:id="@+id/actions_container" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal" - android:paddingEnd="@dimen/screenshot_action_container_padding_right" - android:paddingVertical="@dimen/screenshot_action_container_padding_vertical" + android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" + android:paddingEnd="@dimen/overlay_action_container_padding_right" + android:paddingVertical="@dimen/overlay_action_container_padding_vertical" android:elevation="1dp" android:scrollbars="none" app:layout_constraintHorizontal_bias="0" @@ -53,9 +53,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:animateLayoutChanges="true"> - <include layout="@layout/screenshot_action_chip" + <include layout="@layout/overlay_action_chip" android:id="@+id/remote_copy_chip"/> - <include layout="@layout/screenshot_action_chip" + <include layout="@layout/overlay_action_chip" android:id="@+id/edit_chip"/> </LinearLayout> </HorizontalScrollView> diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml index 856697c5673d..cdf61036e2b7 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -18,7 +18,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:background="?android:colorBackgroundFloating" android:id="@+id/root" android:layout_width="match_parent" @@ -32,7 +31,7 @@ android:text="@string/save" android:layout_marginStart="8dp" android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin" - android:background="@drawable/screenshot_button_background" + android:background="@drawable/overlay_button_background" android:textColor="?android:textColorSecondary" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -46,7 +45,7 @@ android:text="@android:string/cancel" android:layout_marginStart="6dp" android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin" - android:background="@drawable/screenshot_button_background" + android:background="@drawable/overlay_button_background" android:textColor="?android:textColorSecondary" app:layout_constraintStart_toEndOf="@id/save" app:layout_constraintTop_toTopOf="parent" diff --git a/packages/SystemUI/res/layout/screenshot_action_chip.xml b/packages/SystemUI/res/layout/overlay_action_chip.xml index b80469febf3c..6d2d93124234 100644 --- a/packages/SystemUI/res/layout/screenshot_action_chip.xml +++ b/packages/SystemUI/res/layout/overlay_action_chip.xml @@ -14,33 +14,33 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.screenshot.ScreenshotActionChip +<com.android.systemui.screenshot.OverlayActionChip xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/screenshot_action_chip" + android:id="@+id/overlay_action_chip" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/screenshot_action_chip_margin_start" - android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical" + android:layout_marginStart="@dimen/overlay_action_chip_margin_start" + android:paddingVertical="@dimen/overlay_action_chip_margin_vertical" android:layout_gravity="center" android:gravity="center" android:alpha="0.0"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical" + android:paddingVertical="@dimen/overlay_action_chip_padding_vertical" android:background="@drawable/action_chip_background" android:gravity="center"> <ImageView - android:id="@+id/screenshot_action_chip_icon" - android:tint="?android:attr/textColorPrimary" - android:layout_width="@dimen/screenshot_action_chip_icon_size" - android:layout_height="@dimen/screenshot_action_chip_icon_size"/> + android:id="@+id/overlay_action_chip_icon" + android:tint="?attr/overlayButtonTextColor" + android:layout_width="@dimen/overlay_action_chip_icon_size" + android:layout_height="@dimen/overlay_action_chip_icon_size"/> <TextView - android:id="@+id/screenshot_action_chip_text" + android:id="@+id/overlay_action_chip_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:textSize="@dimen/screenshot_action_chip_text_size" - android:textColor="?android:attr/textColorPrimary"/> + android:textSize="@dimen/overlay_action_chip_text_size" + android:textColor="?attr/overlayButtonTextColor"/> </LinearLayout> -</com.android.systemui.screenshot.ScreenshotActionChip> +</com.android.systemui.screenshot.OverlayActionChip> diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml index 227212bd4634..890dbe592fc7 100644 --- a/packages/SystemUI/res/layout/screenshot.xml +++ b/packages/SystemUI/res/layout/screenshot.xml @@ -17,7 +17,7 @@ <com.android.systemui.screenshot.ScreenshotView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/screenshot_frame" - android:theme="@style/Screenshot" + android:theme="@style/FloatingOverlay" android:layout_width="match_parent" android:layout_height="match_parent" android:importantForAccessibility="no"> @@ -30,11 +30,11 @@ android:importantForAccessibility="no"/> <ImageView android:id="@+id/screenshot_actions_background" - android:layout_height="@dimen/screenshot_bg_protection_height" + android:layout_height="@dimen/overlay_bg_protection_height" android:layout_width="match_parent" android:layout_gravity="bottom" android:alpha="0.0" - android:src="@drawable/screenshot_actions_background_protection"/> + android:src="@drawable/overlay_actions_background_protection"/> <ImageView android:id="@+id/screenshot_flash" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml index 8f791c34419d..813bb6018801 100644 --- a/packages/SystemUI/res/layout/screenshot_static.xml +++ b/packages/SystemUI/res/layout/screenshot_static.xml @@ -26,7 +26,7 @@ android:layout_width="0dp" android:elevation="1dp" android:background="@drawable/action_chip_container_background" - android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal" + android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" app:layout_constraintBottom_toBottomOf="@+id/screenshot_actions_container" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/screenshot_actions_container" @@ -35,9 +35,9 @@ android:id="@+id/screenshot_actions_container" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal" - android:paddingEnd="@dimen/screenshot_action_container_padding_right" - android:paddingVertical="@dimen/screenshot_action_container_padding_vertical" + android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" + android:paddingEnd="@dimen/overlay_action_container_padding_right" + android:paddingVertical="@dimen/overlay_action_container_padding_vertical" android:elevation="1dp" android:scrollbars="none" app:layout_constraintHorizontal_bias="0" @@ -50,11 +50,11 @@ android:id="@+id/screenshot_actions" android:layout_width="wrap_content" android:layout_height="wrap_content"> - <include layout="@layout/screenshot_action_chip" + <include layout="@layout/overlay_action_chip" android:id="@+id/screenshot_share_chip"/> - <include layout="@layout/screenshot_action_chip" + <include layout="@layout/overlay_action_chip" android:id="@+id/screenshot_edit_chip"/> - <include layout="@layout/screenshot_action_chip" + <include layout="@layout/overlay_action_chip" android:id="@+id/screenshot_scroll_chip" android:visibility="gone" /> </LinearLayout> @@ -89,7 +89,7 @@ <ImageView android:id="@+id/screenshot_preview" android:visibility="invisible" - android:layout_width="@dimen/screenshot_x_scale" + android:layout_width="@dimen/overlay_x_scale" android:layout_margin="@dimen/overlay_border_width" android:layout_height="wrap_content" android:layout_gravity="center" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 3412722776c2..b318bbc157ca 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -58,9 +58,9 @@ <!-- The color of the text in the Global Actions menu --> <color name="global_actions_alert_text">@color/GM2_red_300</color> - <!-- Global screenshot actions --> - <color name="screenshot_button_ripple">#42FFFFFF</color> - <color name="screenshot_background_protection_start">#80000000</color> <!-- 50% black --> + <!-- Floating overlay actions --> + <color name="overlay_button_ripple">#42FFFFFF</color> + <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black --> <!-- Media --> <color name="media_divider">#85ffffff</color> diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 1f815b79ec30..f7261e70a610 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -47,8 +47,8 @@ <item name="android:textColorSecondary">?android:attr/textColorPrimaryInverse</item> </style> - <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight"> - <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item> + <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight"> + <item name="overlayButtonTextColor">?android:attr/textColorPrimaryInverse</item> </style> <style name="Theme.PeopleTileConfigActivity" parent="@style/Theme.SystemUI"> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index de136de9dd5f..e6ab0ff9bd73 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -204,5 +204,7 @@ <attr name="singleLineVerticalPadding" format="dimension" /> <attr name="textViewId" format="reference" /> </declare-styleable> + + <attr name="overlayButtonTextColor" format="color" /> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 81e3e04b279e..3ab569a19c0c 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -138,9 +138,9 @@ <color name="udfps_enroll_progress">#7DA7F1</color> <color name="udfps_enroll_progress_help">#ffEE675C</color> - <!-- Global screenshot actions --> - <color name="screenshot_button_ripple">#1f000000</color> - <color name="screenshot_background_protection_start">#40000000</color> <!-- 25% black --> + <!-- Floating overlay actions --> + <color name="overlay_button_ripple">#1f000000</color> + <color name="overlay_background_protection_start">#40000000</color> <!-- 25% black --> <!-- GM2 colors --> <color name="GM2_grey_100">#F1F3F4</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index bba616fe24a2..74bb9e45a6f2 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -258,36 +258,36 @@ <!-- Dimensions related to screenshots --> - <!-- The padding on the global screenshot background image --> - <dimen name="screenshot_x_scale">80dp</dimen> - <dimen name="screenshot_bg_protection_height">242dp</dimen> - <dimen name="screenshot_action_container_corner_radius">18dp</dimen> - <dimen name="screenshot_action_container_padding_vertical">4dp</dimen> - <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen> - <dimen name="screenshot_action_container_padding_right">8dp</dimen> - <!-- Radius of the chip background on global screenshot actions --> - <dimen name="screenshot_button_corner_radius">8dp</dimen> + + <dimen name="screenshot_crop_handle_thickness">3dp</dimen> + <dimen name="long_screenshot_action_bar_top_margin">8dp</dimen> + + <!-- Dimensions shared between "overlays" (clipboard and screenshot preview UIs) --> + <!-- Constrained size of the floating overlay preview --> + <dimen name="overlay_x_scale">80dp</dimen> + <!-- Radius of the chip background on floating overlay actions --> + <dimen name="overlay_button_corner_radius">8dp</dimen> <!-- Margin between successive chips --> - <dimen name="screenshot_action_chip_margin_start">8dp</dimen> + <dimen name="overlay_action_chip_margin_start">8dp</dimen> <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) --> - <dimen name="screenshot_action_chip_margin_vertical">4dp</dimen> - <dimen name="screenshot_action_chip_padding_vertical">11dp</dimen> - <dimen name="screenshot_action_chip_icon_size">18sp</dimen> + <dimen name="overlay_action_chip_margin_vertical">4dp</dimen> + <dimen name="overlay_action_chip_padding_vertical">11dp</dimen> + <dimen name="overlay_action_chip_icon_size">18sp</dimen> <!-- Padding on each side of the icon for icon-only chips --> - <dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen> + <dimen name="overlay_action_chip_icon_only_padding_horizontal">14dp</dimen> <!-- Padding at the edges of the chip for icon-and-text chips --> - <dimen name="screenshot_action_chip_padding_horizontal">12dp</dimen> + <dimen name="overlay_action_chip_padding_horizontal">12dp</dimen> <!-- Spacing between chip icon and chip text --> - <dimen name="screenshot_action_chip_spacing">8dp</dimen> - <dimen name="screenshot_action_chip_text_size">14sp</dimen> - <dimen name="screenshot_dismissal_height_delta">80dp</dimen> - <dimen name="screenshot_crop_handle_thickness">3dp</dimen> - <dimen name="long_screenshot_action_bar_top_margin">8dp</dimen> - - <!-- Dimensions shared between "overlays" (clipboard and screenshot preview UIs) --> + <dimen name="overlay_action_chip_spacing">8dp</dimen> + <dimen name="overlay_action_chip_text_size">14sp</dimen> <dimen name="overlay_offset_y">8dp</dimen> <dimen name="overlay_offset_x">16dp</dimen> <dimen name="overlay_preview_elevation">4dp</dimen> + <dimen name="overlay_action_container_margin_horizontal">8dp</dimen> + <dimen name="overlay_bg_protection_height">242dp</dimen> + <dimen name="overlay_action_container_corner_radius">18dp</dimen> + <dimen name="overlay_action_container_padding_vertical">4dp</dimen> + <dimen name="overlay_action_container_padding_right">8dp</dimen> <dimen name="overlay_dismiss_button_elevation">7dp</dimen> <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen> <dimen name="overlay_dismiss_button_margin">8dp</dimen> @@ -295,7 +295,7 @@ <!-- need a negative margin for some of the constraints. should be overlay_border_width * -1 --> <dimen name="overlay_border_width_neg">-4dp</dimen> - <dimen name="clipboard_preview_size">@dimen/screenshot_x_scale</dimen> + <dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen> <!-- The width of the view containing navigation buttons --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 57f1f3f1606c..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"> @@ -664,7 +707,9 @@ <item name="android:windowActivityTransitions">true</item> </style> - <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight"/> + <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight"> + <item name="overlayButtonTextColor">?android:attr/textColorPrimary</item> + </style> <!-- Clipboard overlay's edit text activity. --> <style name="EditTextActivity" parent="@android:style/Theme.DeviceDefault.DayNight"> 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/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 12759f489a26..8b549b43019f 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -74,7 +74,7 @@ import android.widget.TextView; import com.android.internal.policy.PhoneWindow; import com.android.systemui.R; import com.android.systemui.screenshot.FloatingWindowUtil; -import com.android.systemui.screenshot.ScreenshotActionChip; +import com.android.systemui.screenshot.OverlayActionChip; import com.android.systemui.screenshot.TimeoutHandler; import java.io.IOException; @@ -106,12 +106,12 @@ public class ClipboardOverlayController { private final DraggableConstraintLayout mView; private final ImageView mImagePreview; private final TextView mTextPreview; - private final ScreenshotActionChip mEditChip; - private final ScreenshotActionChip mRemoteCopyChip; + private final OverlayActionChip mEditChip; + private final OverlayActionChip mRemoteCopyChip; private final View mActionContainerBackground; private final View mDismissButton; private final LinearLayout mActionContainer; - private final ArrayList<ScreenshotActionChip> mActionChips = new ArrayList<>(); + private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>(); private Runnable mOnSessionCompleteListener; @@ -251,7 +251,7 @@ public class ClipboardOverlayController { for (RemoteAction action : actions) { Intent targetIntent = action.getActionIntent().getIntent(); if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) { - ScreenshotActionChip chip = constructActionChip(action); + OverlayActionChip chip = constructActionChip(action); mActionContainer.addView(chip); mActionChips.add(chip); } @@ -259,9 +259,9 @@ public class ClipboardOverlayController { }); } - private ScreenshotActionChip constructActionChip(RemoteAction action) { - ScreenshotActionChip chip = (ScreenshotActionChip) LayoutInflater.from(mContext).inflate( - R.layout.screenshot_action_chip, mActionContainer, false); + private OverlayActionChip constructActionChip(RemoteAction action) { + OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate( + R.layout.overlay_action_chip, mActionContainer, false); chip.setText(action.getTitle()); chip.setIcon(action.getIcon(), false); chip.setPendingIntent(action.getActionIntent(), this::animateOut); @@ -341,7 +341,7 @@ public class ClipboardOverlayController { mEditChip.setAlpha(1f); ContentResolver resolver = mContext.getContentResolver(); try { - int size = mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale); + int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale); // The width of the view is capped, height maintains aspect ratio, so allow it to be // taller if needed. Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null); @@ -365,7 +365,7 @@ public class ClipboardOverlayController { } private void animateOut() { - getExitAnimation().start(); + mView.dismiss(); } private ValueAnimator getEnterAnimation() { @@ -401,28 +401,6 @@ public class ClipboardOverlayController { return anim; } - private ValueAnimator getExitAnimation() { - ValueAnimator anim = ValueAnimator.ofFloat(0, 1); - - anim.addUpdateListener(animation -> { - mView.setAlpha(1 - animation.getAnimatedFraction()); - final View actionBackground = requireNonNull( - mView.findViewById(R.id.actions_container_background)); - mView.setTranslationX( - -animation.getAnimatedFraction() * actionBackground.getWidth() / 2); - }); - - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - hideImmediate(); - } - }); - - return anim; - } - private void hideImmediate() { // Note this may be called multiple times if multiple dismissal events happen at the same // time. @@ -453,7 +431,7 @@ public class ClipboardOverlayController { } private void resetActionChips() { - for (ScreenshotActionChip chip : mActionChips) { + for (OverlayActionChip chip : mActionChips) { mActionContainer.removeView(chip); } mActionChips.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java index 6a4be6ee5eae..8843462413b5 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java @@ -98,10 +98,23 @@ public class DraggableConstraintLayout extends ConstraintLayout { return mSwipeDetector.onTouchEvent(ev); } + /** + * Dismiss the view, with animation controlled by SwipeDismissHandler + */ + public void dismiss() { + mSwipeDismissHandler.dismiss(); + } + + /** + * Set the callback to be run after view is dismissed + */ public void setOnDismissCallback(Runnable callback) { mOnDismiss = callback; } + /** + * Set the callback to be run when the view is interacted with (e.g. tapped) + */ public void setOnInteractionCallback(Runnable callback) { mOnInteraction = callback; } 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/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java index dec5afdaccfe..c4ea67e0f79e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java @@ -16,6 +16,8 @@ package com.android.systemui.screenshot; +import static java.util.Objects.requireNonNull; + import android.app.PendingIntent; import android.content.Context; import android.graphics.drawable.Icon; @@ -28,10 +30,11 @@ import android.widget.TextView; import com.android.systemui.R; + /** * View for a chip with an icon and text. */ -public class ScreenshotActionChip extends FrameLayout { +public class OverlayActionChip extends FrameLayout { private static final String TAG = "ScreenshotActionChip"; @@ -39,27 +42,27 @@ public class ScreenshotActionChip extends FrameLayout { private TextView mTextView; private boolean mIsPending = false; - public ScreenshotActionChip(Context context) { + public OverlayActionChip(Context context) { this(context, null); } - public ScreenshotActionChip(Context context, AttributeSet attrs) { + public OverlayActionChip(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr) { + public OverlayActionChip(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public ScreenshotActionChip( + public OverlayActionChip( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onFinishInflate() { - mIconView = findViewById(R.id.screenshot_action_chip_icon); - mTextView = findViewById(R.id.screenshot_action_chip_text); + mIconView = requireNonNull(findViewById(R.id.overlay_action_chip_icon)); + mTextView = requireNonNull(findViewById(R.id.overlay_action_chip_text)); updatePadding(mTextView.getText().length() > 0); } @@ -116,15 +119,15 @@ public class ScreenshotActionChip extends FrameLayout { (LinearLayout.LayoutParams) mTextView.getLayoutParams(); if (hasText) { int paddingHorizontal = mContext.getResources().getDimensionPixelSize( - R.dimen.screenshot_action_chip_padding_horizontal); + R.dimen.overlay_action_chip_padding_horizontal); int spacing = mContext.getResources().getDimensionPixelSize( - R.dimen.screenshot_action_chip_spacing); + R.dimen.overlay_action_chip_spacing); iconParams.setMarginStart(paddingHorizontal); iconParams.setMarginEnd(spacing); textParams.setMarginEnd(paddingHorizontal); } else { int paddingHorizontal = mContext.getResources().getDimensionPixelSize( - R.dimen.screenshot_action_chip_icon_only_padding_horizontal); + R.dimen.overlay_action_chip_icon_only_padding_horizontal); iconParams.setMarginStart(paddingHorizontal); iconParams.setMarginEnd(paddingHorizontal); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index e5649a126807..f9827905b69a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -131,9 +131,9 @@ public class ScreenshotView extends FrameLayout implements private final Resources mResources; private final Interpolator mFastOutSlowIn; private final DisplayMetrics mDisplayMetrics; - private final float mCornerSizeX; - private final float mDismissDeltaY; + private final float mFixedSize; private final AccessibilityManager mAccessibilityManager; + private final GestureDetector mSwipeDetector; private int mNavMode; private boolean mOrientationPortrait; @@ -151,23 +151,21 @@ public class ScreenshotView extends FrameLayout implements private LinearLayout mActionsView; private ImageView mBackgroundProtection; private FrameLayout mDismissButton; - private ScreenshotActionChip mShareChip; - private ScreenshotActionChip mEditChip; - private ScreenshotActionChip mScrollChip; - private ScreenshotActionChip mQuickShareChip; + private OverlayActionChip mShareChip; + private OverlayActionChip mEditChip; + private OverlayActionChip mScrollChip; + private OverlayActionChip mQuickShareChip; private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; - private Animator mDismissAnimation; private boolean mPendingSharedTransition; - private GestureDetector mSwipeDetector; private SwipeDismissHandler mSwipeDismissHandler; private InputMonitorCompat mInputMonitor; private InputChannelCompat.InputEventReceiver mInputEventReceiver; private boolean mShowScrollablePreview; private String mPackageName = ""; - private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>(); + private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>(); private PendingInteraction mPendingInteraction; private enum PendingInteraction { @@ -194,9 +192,7 @@ public class ScreenshotView extends FrameLayout implements super(context, attrs, defStyleAttr, defStyleRes); mResources = mContext.getResources(); - mCornerSizeX = mResources.getDimensionPixelSize(R.dimen.screenshot_x_scale); - mDismissDeltaY = mResources.getDimensionPixelSize( - R.dimen.screenshot_dismissal_height_delta); + mFixedSize = mResources.getDimensionPixelSize(R.dimen.overlay_x_scale); // standard material ease mFastOutSlowIn = @@ -474,16 +470,14 @@ public class ScreenshotView extends FrameLayout implements int orientation = mContext.getResources().getConfiguration().orientation; mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT); updateInsets(insets); - int screenshotFixedSize = - mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale); ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams(); if (mOrientationPortrait) { - params.width = screenshotFixedSize; + params.width = (int) mFixedSize; params.height = LayoutParams.WRAP_CONTENT; mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_START); } else { params.width = LayoutParams.WRAP_CONTENT; - params.height = screenshotFixedSize; + params.height = (int) mFixedSize; mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_END); } @@ -500,7 +494,7 @@ public class ScreenshotView extends FrameLayout implements // ratio of preview width, end vs. start size float cornerScale = - mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height()); + mFixedSize / (mOrientationPortrait ? bounds.width() : bounds.height()); final float currentScale = 1 / cornerScale; AnimatorSet dropInAnimation = new AnimatorSet(); @@ -651,7 +645,7 @@ public class ScreenshotView extends FrameLayout implements } catch (RemoteException e) { } - ArrayList<ScreenshotActionChip> chips = new ArrayList<>(); + ArrayList<OverlayActionChip> chips = new ArrayList<>(); mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description)); mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true); @@ -716,7 +710,7 @@ public class ScreenshotView extends FrameLayout implements + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X)); mActionsContainer.setScaleX(containerScale); mActionsContainerBackground.setScaleX(containerScale); - for (ScreenshotActionChip chip : chips) { + for (OverlayActionChip chip : chips) { chip.setAlpha(t); chip.setScaleX(1 / containerScale); // invert to keep size of children constant } @@ -772,8 +766,8 @@ public class ScreenshotView extends FrameLayout implements LayoutInflater inflater = LayoutInflater.from(mContext); for (Notification.Action smartAction : imageData.smartActions) { - ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate( - R.layout.screenshot_action_chip, mActionsView, false); + OverlayActionChip actionChip = (OverlayActionChip) inflater.inflate( + R.layout.overlay_action_chip, mActionsView, false); actionChip.setText(smartAction.title); actionChip.setIcon(smartAction.getIcon(), false); actionChip.setPendingIntent(smartAction.actionIntent, @@ -792,8 +786,8 @@ public class ScreenshotView extends FrameLayout implements void addQuickShareChip(Notification.Action quickShareAction) { if (mPendingInteraction == null) { LayoutInflater inflater = LayoutInflater.from(mContext); - mQuickShareChip = (ScreenshotActionChip) inflater.inflate( - R.layout.screenshot_action_chip, mActionsView, false); + mQuickShareChip = (OverlayActionChip) inflater.inflate( + R.layout.overlay_action_chip, mActionsView, false); mQuickShareChip.setText(quickShareAction.title); mQuickShareChip.setIcon(quickShareAction.getIcon(), false); mQuickShareChip.setOnClickListener(v -> { @@ -894,7 +888,7 @@ public class ScreenshotView extends FrameLayout implements if (mShowScrollablePreview) { Rect scrollableArea = scrollableAreaOnScreen(response); - float scale = mCornerSizeX + float scale = mFixedSize / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams(); @@ -945,7 +939,7 @@ public class ScreenshotView extends FrameLayout implements } boolean isDismissing() { - return (mDismissAnimation != null && mDismissAnimation.isRunning()); + return mSwipeDismissHandler.isDismissing(); } boolean isPendingSharedTransition() { @@ -961,12 +955,6 @@ public class ScreenshotView extends FrameLayout implements Log.d(TAG, "reset screenshot view"); } - if (mDismissAnimation != null && mDismissAnimation.isRunning()) { - if (DEBUG_ANIM) { - Log.d(TAG, "cancelling dismiss animation"); - } - mDismissAnimation.cancel(); - } mSwipeDismissHandler.cancel(); if (DEBUG_WINDOW) { Log.d(TAG, "removing OnComputeInternalInsetsListener"); @@ -994,7 +982,7 @@ public class ScreenshotView extends FrameLayout implements mShareChip.setIsPending(false); mEditChip.setIsPending(false); mPendingInteraction = null; - for (ScreenshotActionChip chip : mSmartChips) { + for (OverlayActionChip chip : mSmartChips) { mActionsView.removeView(chip); } mSmartChips.clear(); @@ -1019,31 +1007,6 @@ public class ScreenshotView extends FrameLayout implements } } - private AnimatorSet createScreenshotTranslateDismissAnimation() { - ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); - alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS); - alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS); - alphaAnim.addUpdateListener(animation -> { - setAlpha(1 - animation.getAnimatedFraction()); - }); - - ValueAnimator xAnim = ValueAnimator.ofFloat(0, 1); - xAnim.setInterpolator(mAccelerateInterpolator); - xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS); - float deltaX = mDirectionLTR - ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth()) - : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX()); - xAnim.addUpdateListener(animation -> { - float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction()); - mScreenshotStatic.setTranslationX(currXDelta); - }); - - AnimatorSet animSet = new AnimatorSet(); - animSet.play(xAnim).with(alphaAnim); - - return animSet; - } - ValueAnimator createScreenshotFadeDismissAnimation() { ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); alphaAnim.addUpdateListener(animation -> { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java index 4e960037bb7b..451fb1311ebb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; import android.animation.Animator; @@ -137,10 +138,20 @@ public class SwipeDismissHandler implements View.OnTouchListener { } /** + * Return whether the view is currently being dismissed + */ + public boolean isDismissing() { + return (mDismissAnimation != null && mDismissAnimation.isRunning()); + } + + /** * Cancel the currently-running dismissal animation, if any. */ public void cancel() { - if (mDismissAnimation != null && mDismissAnimation.isRunning()) { + if (isDismissing()) { + if (DEBUG_ANIM) { + Log.d(TAG, "cancelling dismiss animation"); + } mDismissAnimation.cancel(); } } @@ -182,7 +193,13 @@ public class SwipeDismissHandler implements View.OnTouchListener { // make sure the UI gets all the way off the screen in the direction of movement // (the actions container background is guaranteed to be both the leftmost and // rightmost UI element in LTR and RTL) - float finalX = startX <= 0 ? -1 * mView.getRight() : mDisplayMetrics.widthPixels; + float finalX; + int layoutDir = mView.getContext().getResources().getConfiguration().getLayoutDirection(); + if (startX > 0 || (startX == 0 && layoutDir == View.LAYOUT_DIRECTION_RTL)) { + finalX = mDisplayMetrics.widthPixels; + } else { + finalX = -1 * mView.getRight(); + } float distance = Math.abs(finalX - startX); anim.addUpdateListener(animation -> { 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/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/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/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/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/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/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/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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3549c9ec2e74..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(); @@ -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; @@ -1695,6 +1698,7 @@ public class PackageManagerService extends IPackageManager.Stub mResolveIntentHelper = testParams.resolveIntentHelper; mDexOptHelper = testParams.dexOptHelper; mSuspendPackageHelper = testParams.suspendPackageHelper; + mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); registerObservers(false); @@ -2138,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()) { @@ -3153,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; } @@ -5518,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( diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 1caa76d2435a..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; 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 9bed24d05f3d..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; @@ -145,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: @@ -228,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); @@ -396,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) @@ -441,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); @@ -472,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); } @@ -950,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<>(); 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/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 26c442dc1f47..e18e0020407f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -23,6 +23,7 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.os.FileUtils; import android.os.PersistableBundle; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; @@ -83,7 +84,7 @@ class DevicePolicyData { private static final String ATTR_NEW_USER_DISCLAIMER = "new-user-disclaimer"; // Values of ATTR_NEW_USER_DISCLAIMER - static final String NEW_USER_DISCLAIMER_SHOWN = "shown"; + static final String NEW_USER_DISCLAIMER_ACKNOWLEDGED = "acked"; static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed"; static final String NEW_USER_DISCLAIMER_NEEDED = "needed"; @@ -613,6 +614,28 @@ class DevicePolicyData { } } + boolean isNewUserDisclaimerAcknowledged() { + if (mNewUserDisclaimer == null) { + if (mUserId == UserHandle.USER_SYSTEM) { + return true; + } + Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): mNewUserDisclaimer is null", + mUserId); + return false; + } + switch (mNewUserDisclaimer) { + case NEW_USER_DISCLAIMER_ACKNOWLEDGED: + case NEW_USER_DISCLAIMER_NOT_NEEDED: + return true; + case NEW_USER_DISCLAIMER_NEEDED: + return false; + default: + Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): invalid value %d", mUserId, + mNewUserDisclaimer); + return false; + } + } + void dump(IndentingPrintWriter pw) { pw.println(); pw.println("Enabled Device Admins (User " + mUserId + ", provisioningState: " diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e34178ab9cd2..6f41d42b3d54 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -10978,10 +10978,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void acknowledgeNewUserDisclaimer() { CallerIdentity callerIdentity = getCallerIdentity(); - canManageUsers(callerIdentity); + Preconditions.checkCallAuthorization(canManageUsers(callerIdentity) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); setShowNewUserDisclaimer(callerIdentity.getUserId(), - DevicePolicyData.NEW_USER_DISCLAIMER_SHOWN); + DevicePolicyData.NEW_USER_DISCLAIMER_ACKNOWLEDGED); } private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) { @@ -11014,6 +11015,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean isNewUserDisclaimerAcknowledged() { + CallerIdentity callerIdentity = getCallerIdentity(); + Preconditions.checkCallAuthorization(canManageUsers(callerIdentity) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); + int userId = callerIdentity.getUserId(); + synchronized (getLockObject()) { + DevicePolicyData policyData = getUserData(userId); + return policyData.isNewUserDisclaimerAcknowledged(); + } + } + + @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); @@ -11210,8 +11223,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public int logoutUserInternal() { CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - canManageUsers(caller) || hasCallingOrSelfPermission(permission.CREATE_USERS)); + Preconditions.checkCallAuthorization(canManageUsers(caller) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); int result = logoutUserUnchecked(getCurrentForegroundUserId()); Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result); 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 44b81d4d4100..d2358a08624d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -26,6 +26,8 @@ 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; @@ -41,6 +43,9 @@ 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; @@ -159,11 +164,17 @@ 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())) @@ -322,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. */ @@ -511,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) { @@ -523,7 +574,7 @@ public class GameManagerServiceTests { } GameManagerService.GamePackageConfiguration config = gameManagerService.getConfig(mPackageName); - assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps); + assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps()); } /** @@ -905,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(); 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/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"; |