diff options
88 files changed, 2211 insertions, 932 deletions
diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp index a6e27698e36c..b93227a60d99 100644 --- a/cmds/hid/Android.bp +++ b/cmds/hid/Android.bp @@ -22,5 +22,5 @@ java_binary { name: "hid", wrapper: "hid.sh", srcs: ["**/*.java"], - required: ["libhidcommand_jni"], + jni_libs: ["libhidcommand_jni"], } diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp index da497dcf908e..cec8a0d88b99 100644 --- a/cmds/uinput/Android.bp +++ b/cmds/uinput/Android.bp @@ -25,7 +25,7 @@ java_binary { "src/**/*.java", ":uinputcommand_aidl", ], - required: ["libuinputcommand_jni"], + jni_libs: ["libuinputcommand_jni"], } filegroup { diff --git a/core/api/current.txt b/core/api/current.txt index efede837d7a9..44b3c6232d59 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -21415,6 +21415,7 @@ package android.media { field public static final int ENCODING_AAC_XHE = 16; // 0x10 field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_AC4 = 17; // 0x11 + field @FlaggedApi("android.media.audio.dolby_ac4_level4_encoding_api") public static final int ENCODING_AC4_L4 = 32; // 0x20 field public static final int ENCODING_DEFAULT = 1; // 0x1 field public static final int ENCODING_DOLBY_MAT = 19; // 0x13 field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 91c16c2dffde..8edfc21036ad 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -11957,6 +11957,11 @@ package android.provider { } @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount { + method @FlaggedApi("android.provider.new_default_account_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static java.util.List<android.accounts.Account> getEligibleCloudAccounts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableLocalContacts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static int getNumberOfMovableSimContacts(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveLocalContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver); + method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(allOf={android.Manifest.permission.WRITE_CONTACTS, android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) public static void moveSimContactsToCloudDefaultAccount(@NonNull android.content.ContentResolver); method @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static void setDefaultAccountForNewContacts(@NonNull android.content.ContentResolver, @NonNull android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 38632bdeeff5..bc7ebce5c5c2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -810,6 +810,11 @@ public class Notification implements Parcelable } private static boolean isStandardLayout(int layoutId) { + if (Flags.apiRichOngoing()) { + if (layoutId == R.layout.notification_template_material_progress) { + return true; + } + } return STANDARD_LAYOUTS.contains(layoutId); } @@ -7683,6 +7688,10 @@ public class Notification implements Parcelable return R.layout.notification_template_material_conversation; } + private int getProgressLayoutResource() { + return R.layout.notification_template_material_progress; + } + private int getActionLayoutResource() { return R.layout.notification_material_action; } @@ -11640,6 +11649,37 @@ public class Notification implements Parcelable return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */); } + /** + * @hide + */ + @Override + public RemoteViews makeBigContentView() { + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .allowTextWithProgress(true) + .fillTextsFrom(mBuilder); + + // Replace the text with the big text, but only if the big text is not empty. + RemoteViews contentView = getStandardView(mBuilder.getProgressLayoutResource(), p, + null /* result */); + + // Bind progress start and end icons. + if (mStartIcon != null) { + contentView.setViewVisibility(R.id.notification_progress_start_icon, View.VISIBLE); + contentView.setImageViewIcon(R.id.notification_progress_start_icon, mStartIcon); + } else { + contentView.setViewVisibility(R.id.notification_progress_start_icon, View.GONE); + } + + if (mEndIcon != null) { + contentView.setViewVisibility(R.id.notification_progress_end_icon, View.VISIBLE); + contentView.setImageViewIcon(R.id.notification_progress_end_icon, mEndIcon); + } else { + contentView.setViewVisibility(R.id.notification_progress_end_icon, View.GONE); + } + + return contentView; + } private static @NonNull ArrayList<Bundle> getProgressSegmentsAsBundleList( @Nullable List<Segment> progressSegments) { diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index cf65539946a9..fa26837a0429 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -208,6 +208,36 @@ flag { } flag { + name: "cache_profile_ids" + namespace: "multiuser" + description: "Cache getProfileIds to avoid unnecessary binder calls" + bug: "350421409" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "cache_profile_type" + namespace: "multiuser" + description: "Cache getProfileType to avoid unnecessary binder calls" + bug: "350417403" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "cache_profiles" + namespace: "multiuser" + description: "Cache getProfiles to avoid unnecessary binder calls" + bug: "350419395" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "fix_disabling_of_mu_toggle_when_restriction_applied" namespace: "multiuser" description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled" diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS index 43d3f5466ccf..f11625ed9d61 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -19,3 +19,6 @@ per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS # OverlayProperties per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS + +# Lut related files +per-file *Lut* = file:/graphics/java/android/graphics/OWNERS
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 9e3a9b389b05..b7856303fc05 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -19,6 +19,7 @@ package android.hardware.camera2; import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static android.content.Context.DEVICE_ID_DEFAULT; +import static android.content.Context.DEVICE_ID_INVALID; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; @@ -36,6 +37,7 @@ import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; +import android.content.AttributionSource; import android.content.AttributionSourceState; import android.content.Context; import android.content.pm.PackageManager; @@ -676,8 +678,8 @@ public final class CameraManager { } try { for (String physicalCameraId : physicalCameraIds) { - AttributionSourceState clientAttribution = getClientAttribution(); - clientAttribution.deviceId = DEVICE_ID_DEFAULT; + AttributionSourceState clientAttribution = getClientAttribution(DEVICE_ID_DEFAULT, + /* useContextAttributionSource= */ false); CameraMetadataNative physicalCameraInfo = cameraService.getCameraCharacteristics( physicalCameraId, @@ -974,27 +976,58 @@ public final class CameraManager { } /** - * Constructs an AttributionSourceState with only the uid, pid, and deviceId fields set + * Retrieves the AttributionSourceState to pass to the CameraService. * - * <p>This method is a temporary stopgap in the transition to using AttributionSource. Currently - * AttributionSourceState is only used as a vehicle for passing deviceId, uid, and pid - * arguments.</p> + * @param deviceIdOverride An override of the AttributionSource's deviceId, if not equal to + * DEVICE_ID_INVALID + * @param useContextAttributionSource Whether to return the full attribution source provided by + * the Context. + * + * @hide + */ + public AttributionSourceState getClientAttribution(int deviceIdOverride, + boolean useContextAttributionSource) { + AttributionSource contextAttributionSource = mContext.getAttributionSource(); + if (deviceIdOverride != DEVICE_ID_INVALID) { + contextAttributionSource = contextAttributionSource.withDeviceId(deviceIdOverride); + } + AttributionSourceState contextAttributionSourceState = + contextAttributionSource.asState(); + + if (Flags.useContextAttributionSource() && useContextAttributionSource) { + return contextAttributionSourceState; + } else { + AttributionSourceState clientAttribution = + new AttributionSourceState(); + clientAttribution.uid = USE_CALLING_UID; + clientAttribution.pid = USE_CALLING_PID; + clientAttribution.deviceId = contextAttributionSourceState.deviceId; + clientAttribution.packageName = mContext.getOpPackageName(); + clientAttribution.attributionTag = mContext.getAttributionTag(); + clientAttribution.next = new AttributionSourceState[0]; + return clientAttribution; + } + } + + /** + * Retrieves the AttributionSourceState to pass to the CameraService. + * + * @param useContextAttributionSource Whether to return the full attribution source provided by + * the Context. + * + * @hide + */ + public AttributionSourceState getClientAttribution(boolean useContextAttributionSource) { + return getClientAttribution(DEVICE_ID_INVALID, useContextAttributionSource); + } + + /** + * Retrieves the AttributionSourceState to pass to the CameraService. * * @hide */ public AttributionSourceState getClientAttribution() { - // TODO: Send the full contextAttribution over aidl, remove USE_CALLING_* - AttributionSourceState contextAttribution = - mContext.getAttributionSource().asState(); - AttributionSourceState clientAttribution = - new AttributionSourceState(); - clientAttribution.uid = USE_CALLING_UID; - clientAttribution.pid = USE_CALLING_PID; - clientAttribution.deviceId = contextAttribution.deviceId; - clientAttribution.packageName = mContext.getOpPackageName(); - clientAttribution.attributionTag = mContext.getAttributionTag(); - clientAttribution.next = new AttributionSourceState[0]; - return clientAttribution; + return getClientAttribution(DEVICE_ID_INVALID, /* useContextAttributionSource= */ false); } /** @@ -1049,7 +1082,7 @@ public final class CameraManager { } AttributionSourceState clientAttribution = - getClientAttribution(); + getClientAttribution(/* useContextAttributionSource= */ true); cameraUser = cameraService.connectDevice( callbacks, diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index d557046280d9..98d58d0c991f 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3028,6 +3028,13 @@ public final class ContactsContract { @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) public static final class DefaultAccount { /** + * no public constructor since this is a utility class + */ + private DefaultAccount() { + + } + + /** * Key in the outgoing Bundle for the default account list. * * @hide @@ -3063,11 +3070,6 @@ public final class ContactsContract { public static final String QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD = "queryDefaultAccountForNewContacts"; - private DefaultAccount() { - - } - - /** * Represents the state of the default account, and the actual {@link Account} if it's * a cloud account. @@ -3356,6 +3358,188 @@ public final class ContactsContract { nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, SET_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, extras); } + + /** + * Get a list of cloud accounts that is eligible to set as default account with state of + * {@link DefaultAccountAndState#DEFAULT_ACCOUNT_STATE_CLOUD}. May be empty but never + * null. + * + * @param resolver content resolver to query. + * @return a of cloud accounts that is eligible to set as default account with state of + * {@link DefaultAccountAndState#DEFAULT_ACCOUNT_STATE_CLOUD}. + * @throws RuntimeException if the query fails. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @SystemApi + public static @NonNull List<Account> getEligibleCloudAccounts( + @NonNull ContentResolver resolver) { + Bundle response = nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, + QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD, null, null); + List<Account> result = response.getParcelableArrayList( + KEY_ELIGIBLE_DEFAULT_ACCOUNTS, Account.class); + if (result == null) { + return new ArrayList<>(); + } + return result; + } + + + + /** + * The method to invoke to move local {@link RawContacts} and {@link Groups} from local + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD = + "moveLocalContactsToCloudDefaultAccount"; + + /** + * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the + * Cloud Default Account (if any). + * @param resolver the ContentResolver to query. + * @throws RuntimeException if it fails to move contacts to the default account. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static void moveLocalContactsToCloudDefaultAccount( + @NonNull ContentResolver resolver) { + + Bundle extras = new Bundle(); + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + MOVE_LOCAL_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD, + null, + extras); + } + + /** + * The method to invoke to move {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD = + "moveSimContactsToCloudDefaultAccount"; + + /** + * Move {@link RawContacts} and {@link Groups} (if any) from the local account to the + * Cloud Default Account (if any). + * @param resolver the ContentResolver to query. + * @throws RuntimeException if it fails to move contacts to the default account. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static void moveSimContactsToCloudDefaultAccount( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + MOVE_SIM_CONTACTS_TO_CLOUD_DEFAULT_ACCOUNT_METHOD, + /* arg= */ null, + /* extras= */ null); + } + + /** + * The method to invoke to get the number of {@link RawContacts} that are in local + * account(s) and movable to the Cloud Default Account (if any). + * + * @hide + */ + public static final String GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD = + "getNumberOfMovableLocalContacts"; + + /** + * The result key for moving local {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS = + "key_number_of_movable_local_contacts"; + + /** + * Gets the number of {@link RawContacts} in the local account(s) which may be moved + * using {@link DefaultAccount#moveLocalContactsToCloudDefaultAccount} (if any). + * @param resolver the ContentResolver to query. + * @return the number of {@link RawContacts} in the local account(s), or 0 if there is + * no Cloud Default Account. + * @throws RuntimeException if it fails get the number of movable local contacts. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static int getNumberOfMovableLocalContacts( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + GET_NUMBER_OF_MOVABLE_LOCAL_CONTACTS_METHOD, + /* arg= */ null, + /* extras= */ null); + return result.getInt(KEY_NUMBER_OF_MOVABLE_LOCAL_CONTACTS, + /* defaultValue= */ 0); + } + + /** + * The method to invoke to get the number of {@link RawContacts} that are in SIM + * account(s) and movable to the Cloud Default Account (if any). + * + * @hide + */ + public static final String GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD = + "getNumberOfMovableSimContacts"; + + /** + * The result key for moving local {@link RawContacts} and {@link Groups} from SIM + * account(s) to the Cloud Default Account (if any). + * + * @hide + */ + public static final String KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS = + "key_number_of_movable_sim_contacts"; + + /** + * Gets the number of {@link RawContacts} in the SIM account(s) which may be moved using + * {@link DefaultAccount#moveSimContactsToCloudDefaultAccount} (if any). + * @param resolver the ContentResolver to query. + * @return the number of {@link RawContacts} in the SIM account(s), or 0 if there is + * no Cloud Default Account. + * @throws RuntimeException if it fails get the number of movable sim contacts. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED) + @RequiresPermission(allOf = {android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS}) + public static int getNumberOfMovableSimContacts( + @NonNull ContentResolver resolver) { + Bundle result = nullSafeCall( + resolver, + ContactsContract.AUTHORITY_URI, + GET_NUMBER_OF_MOVABLE_SIM_CONTACTS_METHOD, + /* arg= */ null, + /* extras= */ null); + return result.getInt(KEY_NUMBER_OF_MOVABLE_SIM_CONTACTS, + /* defaultValue= */ 0); + } + } /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 53935e810913..8a8022c0206a 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -449,6 +449,13 @@ public final class Display { public static final int TYPE_VIRTUAL = 5; /** + * The maximum display type value. + * Helpful to get all possible display values. + * @hide + */ + public static final int TYPE_MAX = TYPE_VIRTUAL; + + /** * Display state: The display state is unknown. * * @see #getState diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index 7cda3a36da95..8ac68abd8d8e 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -34,8 +34,6 @@ public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; private final float mProgress; - private final float mVelocityX; - private final float mVelocityY; private final boolean mTriggerBack; @BackEvent.SwipeEdge @@ -51,10 +49,6 @@ public final class BackMotionEvent implements Parcelable { * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. - * @param velocityX X velocity computed from the touch point of this event. - * Value in pixels/second. {@link Float#NaN} if was not computed. - * @param velocityY Y velocity computed from the touch point of this event. - * Value in pixels/second. {@link Float#NaN} if was not computed. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. * @param departingAnimationTarget The remote animation target of the departing @@ -64,16 +58,12 @@ public final class BackMotionEvent implements Parcelable { float touchX, float touchY, float progress, - float velocityX, - float velocityY, boolean triggerBack, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; - mVelocityX = velocityX; - mVelocityY = velocityY; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; mDepartingAnimationTarget = departingAnimationTarget; @@ -83,8 +73,6 @@ public final class BackMotionEvent implements Parcelable { mTouchX = in.readFloat(); mTouchY = in.readFloat(); mProgress = in.readFloat(); - mVelocityX = in.readFloat(); - mVelocityY = in.readFloat(); mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); @@ -113,8 +101,6 @@ public final class BackMotionEvent implements Parcelable { dest.writeFloat(mTouchX); dest.writeFloat(mTouchY); dest.writeFloat(mProgress); - dest.writeFloat(mVelocityX); - dest.writeFloat(mVelocityY); dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); @@ -145,24 +131,6 @@ public final class BackMotionEvent implements Parcelable { } /** - * Returns the X velocity computed from the touch point. - * - * @return value in pixels/second or {@link Float#NaN} if was not computed. - */ - public float getVelocityX() { - return mVelocityX; - } - - /** - * Returns the Y velocity computed from the touch point. - * - * @return value in pixels/second or {@link Float#NaN} if was not computed. - */ - public float getVelocityY() { - return mVelocityY; - } - - /** * Returns whether the back arrow is in the triggered state or not * * @return boolean indicating whether the back arrow is in the triggered state or not @@ -195,8 +163,6 @@ public final class BackMotionEvent implements Parcelable { + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress - + ", mVelocityX=" + mVelocityX - + ", mVelocityY=" + mVelocityY + ", mTriggerBack=" + mTriggerBack + ", mSwipeEdge" + mSwipeEdge + ", mDepartingAnimationTarget" + mDepartingAnimationTarget diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index eb23af27a24d..290c494b1bff 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -48,8 +48,6 @@ public class BackTouchTracker { */ private float mInitTouchX; private float mInitTouchY; - private float mLatestVelocityX; - private float mLatestVelocityY; private float mStartThresholdX; private int mSwipeEdge; private boolean mShouldUpdateStartLocation = false; @@ -58,7 +56,7 @@ public class BackTouchTracker { /** * Updates the tracker with a new motion event. */ - public void update(float touchX, float touchY, float velocityX, float velocityY) { + public void update(float touchX, float touchY) { /** * If back was previously cancelled but the user has started swiping in the forward * direction again, restart back. @@ -73,8 +71,6 @@ public class BackTouchTracker { } mLatestTouchX = touchX; mLatestTouchY = touchY; - mLatestVelocityX = velocityX; - mLatestVelocityY = velocityY; } /** Sets whether the back gesture is past the trigger threshold. */ @@ -156,8 +152,6 @@ public class BackTouchTracker { /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, /* progress = */ 0, - /* velocityX = */ 0, - /* velocityY = */ 0, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, /* departingAnimationTarget = */ target); @@ -242,8 +236,6 @@ public class BackTouchTracker { /* touchX = */ mLatestTouchX, /* touchY = */ mLatestTouchY, /* progress = */ progress, - /* velocityX = */ mLatestVelocityX, - /* velocityY = */ mLatestVelocityY, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, /* departingAnimationTarget = */ null); diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 771dc7a99c0e..66c35e2fe837 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -237,7 +237,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { mIOnBackInvokedCallback.onBackStarted( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), - backEvent.getProgress(), 0f, 0f, false, backEvent.getSwipeEdge(), + backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); @@ -249,7 +249,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { mIOnBackInvokedCallback.onBackProgressed( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), - backEvent.getProgress(), 0f, 0f, false, backEvent.getSwipeEdge(), + backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 51bc7d5c571b..44a374fb7c20 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -124,7 +124,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { if (!isBackGestureInProgress() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) { return; } - mTouchTracker.update(ev.getX(), ev.getY(), Float.NaN, Float.NaN); + mTouchTracker.update(ev.getX(), ev.getY()); if (mTouchTracker.shouldUpdateStartLocation()) { // Reset the start location on the first event after starting back, so that // the beginning of the animation feels smooth. diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java new file mode 100644 index 000000000000..4d88546a1bd8 --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.content.pm.ActivityInfo.Config; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Log; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This is used by NotificationProgressBar for displaying a custom background. It composes of + * segments, which have non-zero length, and points, which have zero length. + * + * @see Segment + * @see Point + */ +public final class NotificationProgressDrawable extends Drawable { + private static final String TAG = "NotifProgressDrawable"; + + private State mState; + private boolean mMutated; + + private final ArrayList<Part> mParts = new ArrayList<>(); + + private final Rect mPointRect = new Rect(); + private final RectF mPointRectF = new RectF(); + + private final Paint mStrokePaint = new Paint(); + private final Paint mDashedStrokePaint = new Paint(); + private final Paint mFillPaint = new Paint(); + + { + mStrokePaint.setStyle(Paint.Style.STROKE); + mStrokePaint.setStrokeCap(Paint.Cap.ROUND); + + mDashedStrokePaint.setStyle(Paint.Style.STROKE); + + mFillPaint.setStyle(Paint.Style.FILL); + } + + private int mAlpha; + + public NotificationProgressDrawable() { + this(new State(), null); + } + + /** + * <p>Set the stroke width and default color for the drawable.</p> + * <p>Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @see #mutate() + * @see #setStroke(int, int, float, float) + */ + public void setStroke(int width, @ColorInt int color) { + setStroke(width, color, 0, 0); + } + + /** + * <p>Set the stroke width and default color for the drawable. This method can also be used + * to dash the stroke for the dashed segments.</p> + * <p>Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes + * @param dashGap The gap in pixels between dashes + * @see #mutate() + * @see #setStroke(int, int) + */ + public void setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) { + mState.setStroke(width, color, dashWidth, dashGap); + setStrokeInternal(width, dashWidth, dashGap); + } + + /** + * <p>Set the stroke default color for the drawable.</p> + * <p>Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> + * + * @param color The color of the stroke + * @see #mutate() + * @see #setStroke(int, int, float, float) + */ + public void setStrokeDefaultColor(@ColorInt int color) { + mState.mStrokeColor = color; + } + + /** + * <p>Set the point rect default color for the drawable.</p> + * <p>Note: changing this property will affect all instances of a drawable loaded from a + * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> + * + * @param color The color of the point rect + * @see #mutate() + */ + public void setPointRectDefaultColor(@ColorInt int color) { + mState.mPointRectColor = color; + } + + private void setStrokeInternal(int width, float dashWidth, float dashGap) { + mStrokePaint.setStrokeWidth(width); + + mDashedStrokePaint.setStrokeWidth(width); + DashPathEffect e = null; + if (dashWidth > 0) { + e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0); + } + mDashedStrokePaint.setPathEffect(e); + + invalidateSelf(); + } + + /** + * + */ + public void setParts(@NonNull Part... parts) { + mParts.clear(); + mParts.addAll(Arrays.asList(parts)); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final float pointRadius = + mState.mPointRadius; // how big the point icon will be, halved + + // generally, we will start drawing at (x, y) and end at (x+w, y) + float x = (float) getBounds().left; + final float centerY = (float) getBounds().centerY(); + final float totalWidth = (float) getBounds().width(); + + final int numParts = mParts.size(); + for (int iPart = 0; iPart < numParts; iPart++) { + final Part part = mParts.get(iPart); + final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1); + final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1); + if (part instanceof Segment segment) { + final float segWidth = segment.mFraction * totalWidth; + // Advance the start position to account for a point immediately prior. + final float startOffset = getSegStartOffset(prevPart, pointRadius, + mState.mSegPointGap); + final float start = x + startOffset; + // Retract the end position to account for the padding and a point immediately + // after. + final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap, + mState.mSegSegGap); + final float end = x + segWidth - endOffset; + + // Transparent is not allowed (and also is the default in the data), so use that + // as a sentinel to be replaced by default + mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mStrokeColor); + mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mStrokeColor); + + // Leave space for the rounded line cap which extends beyond start/end. + final float capWidth = mStrokePaint.getStrokeWidth() / 2F; + + canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, + segment.mDashed ? mDashedStrokePaint : mStrokePaint); + + // Advance the current position to account for the segment's fraction of the total + // width (ignoring offset and padding) + x += segWidth; + } else if (part instanceof Point point) { + mPointRect.set((int) (x - pointRadius), (int) (centerY - pointRadius), + (int) (x + pointRadius), (int) (centerY + pointRadius)); + if (point.mIcon != null) { + point.mIcon.setBounds(mPointRect); + point.mIcon.draw(canvas); + } else { + // TODO: b/367804171 - actually use a vector asset for the default point + // rather than drawing it as a box? + mPointRectF.set(mPointRect); + final float inset = mState.mPointRectInset; + final float cornerRadius = mState.mPointRectCornerRadius; + mPointRectF.inset(inset, inset); + + mFillPaint.setColor(point.mColor != Color.TRANSPARENT ? point.mColor + : mState.mPointRectColor); + + canvas.drawRoundRect(mPointRectF, cornerRadius, cornerRadius, mFillPaint); + } + } + } + } + + private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap) { + return (prevPart instanceof Point) ? pointRadius + segPointGap : 0F; + } + + private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap, + float segSegGap) { + if (nextPart == null) return 0F; + return (nextPart instanceof Point) ? segPointGap + pointRadius : segSegGap; + } + + @Override + public @Config int getChangingConfigurations() { + return super.getChangingConfigurations() | mState.getChangingConfigurations(); + } + + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + invalidateSelf(); + } + } + + @Override + public int getAlpha() { + return mAlpha; + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + // NO-OP + } + + @Override + public int getOpacity() { + // This method is deprecated. Hence we return UNKNOWN. + return PixelFormat.UNKNOWN; + } + + @Override + public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs, theme); + + mState.setDensity(resolveDensity(r, 0)); + + final TypedArray a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawable); + updateStateFromTypedArray(a); + a.recycle(); + + inflateChildElements(r, parser, attrs, theme); + + updateLocalState(); + } + + @Override + public void applyTheme(@NonNull Theme t) { + super.applyTheme(t); + + final State state = mState; + if (state == null) { + return; + } + + state.setDensity(resolveDensity(t.getResources(), 0)); + + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrs, R.styleable.NotificationProgressDrawable); + updateStateFromTypedArray(a); + a.recycle(); + } + + applyThemeChildElements(t); + + updateLocalState(); + } + + @Override + public boolean canApplyTheme() { + return (mState.canApplyTheme()) || super.canApplyTheme(); + } + + private void updateStateFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); + + state.mSegSegGap = a.getDimension(R.styleable.NotificationProgressDrawable_segSegGap, + state.mSegSegGap); + state.mSegPointGap = a.getDimension(R.styleable.NotificationProgressDrawable_segPointGap, + state.mSegPointGap); + } + + private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + TypedArray a; + int type; + + final int innerDepth = parser.getDepth() + 1; + int depth; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth) { + continue; + } + + String name = parser.getName(); + + if (name.equals("segments")) { + a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawableSegments); + updateSegmentsFromTypedArray(a); + a.recycle(); + } else if (name.equals("points")) { + a = obtainAttributes(r, theme, attrs, + R.styleable.NotificationProgressDrawablePoints); + updatePointsFromTypedArray(a); + a.recycle(); + } else { + Log.w(TAG, "Bad element under NotificationProgressDrawable: " + name); + } + } + } + + private void applyThemeChildElements(Theme t) { + final State state = mState; + + if (state.mThemeAttrsSegments != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrsSegments, R.styleable.NotificationProgressDrawableSegments); + updateSegmentsFromTypedArray(a); + a.recycle(); + } + + if (state.mThemeAttrsPoints != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints); + updateSegmentsFromTypedArray(a); + a.recycle(); + } + } + + private void updateSegmentsFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrsSegments = a.extractThemeAttrs(); + + final int width = a.getDimensionPixelSize( + R.styleable.NotificationProgressDrawableSegments_width, state.mStrokeWidth); + final float dashWidth = a.getDimension( + R.styleable.NotificationProgressDrawableSegments_dashWidth, state.mStrokeDashWidth); + + final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color, + state.mStrokeColor); + + if (dashWidth != 0.0f) { + final float dashGap = a.getDimension( + R.styleable.NotificationProgressDrawableSegments_dashGap, state.mStrokeDashGap); + setStroke(width, color, dashWidth, dashGap); + } else { + setStroke(width, color); + } + } + + private void updatePointsFromTypedArray(TypedArray a) { + final State state = mState; + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrsPoints = a.extractThemeAttrs(); + + state.mPointRadius = a.getDimension(R.styleable.NotificationProgressDrawablePoints_radius, + state.mPointRadius); + state.mPointRectInset = a.getDimension(R.styleable.NotificationProgressDrawablePoints_inset, + state.mPointRectInset); + state.mPointRectCornerRadius = a.getDimension( + R.styleable.NotificationProgressDrawablePoints_cornerRadius, + state.mPointRectCornerRadius); + state.mPointRectColor = a.getColor(R.styleable.NotificationProgressDrawablePoints_color, + state.mPointRectColor); + } + + static int resolveDensity(@Nullable Resources r, int parentDensity) { + final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi; + return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi; + } + + /** + * Scales a floating-point pixel value from the source density to the + * target density. + */ + private static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) { + return pixels * targetDensity / sourceDensity; + } + + /** + * Scales a pixel value from the source density to the target density. + * <p> + * Optionally, when {@code isSize} is true, handles the resulting pixel value as a size, + * which is rounded to the closest positive integer. + * <p> + * Note: Iteratively applying density changes could result in drift of the pixel values due + * to rounding, especially for paddings which are truncated. Therefore it should be avoided. + * This isn't an issue for the notifications because the inflation pipeline reinflates + * notification views on density change. + */ + private static int scaleFromDensity( + int pixels, int sourceDensity, int targetDensity, boolean isSize) { + if (pixels == 0 || sourceDensity == targetDensity) { + return pixels; + } + + final float result = pixels * targetDensity / (float) sourceDensity; + if (!isSize) { + return (int) result; + } + + final int rounded = Math.round(result); + if (rounded != 0) { + return rounded; + } else if (pixels > 0) { + return 1; + } else { + return -1; + } + } + + /** + * A part of the progress bar, which is either a S{@link Segment} with non-zero length, or a + * {@link Point} with zero length. + */ + public interface Part { + + } + + /** + * A segment is a part of the progress bar with non-zero length. For example, it can + * represent a portion in a navigation journey with certain traffic condition. + */ + public static final class Segment implements Part { + private final float mFraction; + @ColorInt private final int mColor; + private final boolean mDashed; + + public Segment(float fraction) { + this(fraction, Color.TRANSPARENT); + } + + public Segment(float fraction, @ColorInt int color) { + this(fraction, color, false); + } + + public Segment(float fraction, @ColorInt int color, boolean dashed) { + mFraction = fraction; + mColor = color; + mDashed = dashed; + } + + public float getFraction() { + return this.mFraction; + } + + public int getColor() { + return this.mColor; + } + + public boolean getDashed() { + return this.mDashed; + } + + @Override + public String toString() { + return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed=" + + this.mDashed + ')'; + } + } + + /** + * A point is a part of the progress bar with zero length. Points are designated points within a + * progressbar to visualize distinct stages or milestones. For example, a stop in a multi-stop + * ride-share journey. + */ + public static final class Point implements Part { + @Nullable + private final Drawable mIcon; + @ColorInt private final int mColor; + + public Point(@Nullable Drawable icon) { + this(icon, Color.TRANSPARENT); + } + + public Point(@Nullable Drawable icon, @ColorInt int color) { + mIcon = icon; + mColor = color; + } + + @Nullable + public Drawable getIcon() { + return this.mIcon; + } + + @Override + public String toString() { + return "Point(icon=" + this.mIcon + ", color=" + this.mColor + ')'; + } + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState = new State(mState, null); + updateLocalState(); + mMutated = true; + } + return this; + } + + @Override + public void clearMutated() { + super.clearMutated(); + mMutated = false; + } + + static final class State extends ConstantState { + @Config + int mChangingConfigurations; + float mSegSegGap = 0.0f; + float mSegPointGap = 0.0f; + int mStrokeWidth = 0; + int mStrokeColor; + float mStrokeDashWidth = 0.0f; + float mStrokeDashGap = 0.0f; + float mPointRadius; + float mPointRectInset; + float mPointRectCornerRadius; + int mPointRectColor; + + int[] mThemeAttrs; + int[] mThemeAttrsSegments; + int[] mThemeAttrsPoints; + + int mDensity = DisplayMetrics.DENSITY_DEFAULT; + + State() { + } + + State(@NonNull State orig, @Nullable Resources res) { + mChangingConfigurations = orig.mChangingConfigurations; + mStrokeColor = orig.mStrokeColor; + mStrokeWidth = orig.mStrokeWidth; + mStrokeDashWidth = orig.mStrokeDashWidth; + mStrokeDashGap = orig.mStrokeDashGap; + mPointRadius = orig.mPointRadius; + mPointRectInset = orig.mPointRectInset; + mPointRectCornerRadius = orig.mPointRectCornerRadius; + mPointRectColor = orig.mPointRectColor; + + mThemeAttrs = orig.mThemeAttrs; + mThemeAttrsSegments = orig.mThemeAttrsSegments; + mThemeAttrsPoints = orig.mThemeAttrsPoints; + + mDensity = resolveDensity(res, orig.mDensity); + if (orig.mDensity != mDensity) { + applyDensityScaling(orig.mDensity, mDensity); + } + } + + private void applyDensityScaling(int sourceDensity, int targetDensity) { + if (mStrokeWidth > 0) { + mStrokeWidth = scaleFromDensity( + mStrokeWidth, sourceDensity, targetDensity, true); + } + if (mStrokeDashWidth > 0) { + mStrokeDashWidth = scaleFromDensity( + mStrokeDashWidth, sourceDensity, targetDensity); + } + if (mStrokeDashGap > 0) { + mStrokeDashGap = scaleFromDensity( + mStrokeDashGap, sourceDensity, targetDensity); + } + if (mPointRadius > 0) { + mPointRadius = scaleFromDensity( + mPointRadius, sourceDensity, targetDensity); + } + if (mPointRectInset > 0) { + mPointRectInset = scaleFromDensity( + mPointRectInset, sourceDensity, targetDensity); + } + if (mPointRectCornerRadius > 0) { + mPointRectCornerRadius = scaleFromDensity( + mPointRectCornerRadius, sourceDensity, targetDensity); + } + } + + @NonNull + @Override + public Drawable newDrawable() { + return new NotificationProgressDrawable(this, null); + } + + @Override + public Drawable newDrawable(@Nullable Resources res) { + // If this drawable is being created for a different density, + // just create a new constant state and call it a day. + final State state; + final int density = resolveDensity(res, mDensity); + if (density != mDensity) { + state = new State(this, res); + } else { + state = this; + } + + return new NotificationProgressDrawable(state, res); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + + @Override + public boolean canApplyTheme() { + return mThemeAttrs != null || mThemeAttrsSegments != null || mThemeAttrsPoints != null + || super.canApplyTheme(); + } + + public void setDensity(int targetDensity) { + if (mDensity != targetDensity) { + final int sourceDensity = mDensity; + mDensity = targetDensity; + + applyDensityScaling(sourceDensity, targetDensity); + } + } + + public void setStroke(int width, int color, float dashWidth, float dashGap) { + mStrokeWidth = width; + mStrokeColor = color; + mStrokeDashWidth = dashWidth; + mStrokeDashGap = dashGap; + } + } + + @Override + public ConstantState getConstantState() { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; + } + + /** + * Creates a new themed NotificationProgressDrawable based on the specified constant state. + * <p> + * The resulting drawable is guaranteed to have a new constant state. + * + * @param state Constant state from which the drawable inherits + */ + private NotificationProgressDrawable(@NonNull State state, @Nullable Resources res) { + mState = state; + + updateLocalState(); + } + + private void updateLocalState() { + final State state = mState; + + mStrokePaint.setStrokeWidth(state.mStrokeWidth); + + if (state.mStrokeDashWidth != 0.0f) { + final DashPathEffect e = new DashPathEffect( + new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0); + mDashedStrokePaint.setPathEffect(e); + } + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 4d2195deebae..2bc32657bd4a 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -308,6 +308,7 @@ cc_library_shared_for_libandroid_runtime { "spatializer-aidl-cpp", "av-types-aidl-cpp", "android.hardware.camera.device@3.2", + "camera_platform_flags_c_lib", "libandroid_net", "libbattery", "libnetdutils", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index af106235bd77..af393fdc5ad4 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -119,3 +119,4 @@ per-file android_tracing_Perfetto* = file:platform/development:/tools/winscope/O # ApplicationSharedMemory per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS +per-file *PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 3f74fac35bb7..10e49efee92e 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -25,6 +25,7 @@ #include <binder/Parcel.h> #include <camera/Camera.h> #include <camera/StringUtils.h> +#include <com_android_internal_camera_flags.h> #include <cutils/properties.h> #include <gui/GLConsumer.h> #include <gui/Surface.h> @@ -37,6 +38,7 @@ #include "jni.h" using namespace android; +namespace flags = com::android::internal::camera::flags; enum { // Keep up to date with Camera.java @@ -527,14 +529,19 @@ void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *b } static bool attributionSourceStateForJavaParcel(JNIEnv *env, jobject jClientAttributionParcel, + bool useContextAttributionSource, AttributionSourceState &clientAttribution) { const Parcel *clientAttributionParcel = parcelForJavaObject(env, jClientAttributionParcel); if (clientAttribution.readFromParcel(clientAttributionParcel) != ::android::OK) { jniThrowRuntimeException(env, "Fail to unparcel AttributionSourceState"); return false; } - clientAttribution.uid = Camera::USE_CALLING_UID; - clientAttribution.pid = Camera::USE_CALLING_PID; + + if (!(useContextAttributionSource && flags::use_context_attribution_source())) { + clientAttribution.uid = Camera::USE_CALLING_UID; + clientAttribution.pid = Camera::USE_CALLING_PID; + } + return true; } @@ -542,7 +549,9 @@ static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz jobject jClientAttributionParcel, jint devicePolicy) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ false, + clientAttribution)) { return 0; } return Camera::getNumberOfCameras(clientAttribution, devicePolicy); @@ -553,7 +562,9 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, jin jobject jClientAttributionParcel, jint devicePolicy, jobject info_obj) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ false, + clientAttribution)) { return; } @@ -587,7 +598,9 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj jobject jClientAttributionParcel, jint devicePolicy) { AttributionSourceState clientAttribution; - if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, clientAttribution)) { + if (!attributionSourceStateForJavaParcel(env, jClientAttributionParcel, + /* useContextAttributionSource= */ true, + clientAttribution)) { return -EACCES; } diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index a9b19062b764..704aef3cd131 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -50,6 +50,7 @@ #define ENCODING_DTS_HD_MA 29 #define ENCODING_DTS_UHD_P2 30 #define ENCODING_DSD 31 +#define ENCODING_AC4_L4 32 #define ENCODING_INVALID 0 #define ENCODING_DEFAULT 1 @@ -95,6 +96,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_AAC_XHE; case ENCODING_AC4: return AUDIO_FORMAT_AC4; + case ENCODING_AC4_L4: + return AUDIO_FORMAT_AC4_L4; case ENCODING_E_AC3_JOC: return AUDIO_FORMAT_E_AC3_JOC; case ENCODING_DEFAULT: @@ -177,6 +180,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_AAC_XHE; case AUDIO_FORMAT_AC4: return ENCODING_AC4; + case AUDIO_FORMAT_AC4_L4: + return ENCODING_AC4_L4; case AUDIO_FORMAT_E_AC3_JOC: return ENCODING_E_AC3_JOC; case AUDIO_FORMAT_MAT: diff --git a/core/res/res/drawable/notification_progress_icon_background.xml b/core/res/res/drawable/notification_progress_icon_background.xml new file mode 100644 index 000000000000..8e843bea3d6a --- /dev/null +++ b/core/res/res/drawable/notification_progress_icon_background.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <solid android:color="#00000000" /> + <corners android:radius="4dp" /> +</shape>
\ No newline at end of file diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml new file mode 100644 index 000000000000..b413c701d6c5 --- /dev/null +++ b/core/res/res/layout/notification_template_material_progress.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + android:tag="progress" + > + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/notification_content_margin" + android:orientation="vertical" + > + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="top" + > + + <include layout="@layout/notification_template_header" /> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_content_margin_top" + android:orientation="vertical" + > + + <include layout="@layout/notification_template_part_line1" /> + + <include layout="@layout/notification_template_text_multiline" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:layout_marginTop="@dimen/notification_progress_margin_top" + android:orientation="horizontal"> + + <com.android.internal.widget.CachingIconView + android:id="@+id/notification_progress_start_icon" + android:layout_width="@dimen/notification_progress_icon_size" + android:layout_height="@dimen/notification_progress_icon_size" + android:background="@drawable/notification_progress_icon_background" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:layout_marginEnd="@dimen/notification_progress_margin_horizontal" + android:scaleType="centerCrop" + android:maxDrawableWidth="@dimen/notification_progress_icon_size" + android:maxDrawableHeight="@dimen/notification_progress_icon_size" + /> + + + <include + android:layout_width="0dp" + android:layout_height="@dimen/notification_progress_bar_height" + layout="@layout/notification_template_progress" + android:layout_weight="1" + /> + + <com.android.internal.widget.CachingIconView + android:id="@+id/notification_progress_end_icon" + android:layout_width="@dimen/notification_progress_icon_size" + android:layout_height="@dimen/notification_progress_icon_size" + android:background="@drawable/notification_progress_icon_background" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:layout_marginStart="@dimen/notification_progress_margin_horizontal" + android:maxDrawableWidth="@dimen/notification_progress_icon_size" + android:maxDrawableHeight="@dimen/notification_progress_icon_size" + /> + </LinearLayout> + </LinearLayout> + + <include layout="@layout/notification_template_right_icon" /> + </FrameLayout> + + <ViewStub + android:layout="@layout/notification_material_reply_text" + android:id="@+id/notification_material_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + <include + layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_content_margin" + /> + + <include layout="@layout/notification_material_action_list" /> + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 440219de9561..02f9f3c5f0db 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7720,6 +7720,44 @@ <attr name="animation" /> </declare-styleable> + <!-- ================================== --> + <!-- NotificationProgressDrawable class --> + <!-- ================================== --> + + <!-- Drawable used to render a segmented bar, with segments and points. --> + <!-- @hide internal use only --> + <declare-styleable name="NotificationProgressDrawable"> + <!-- Default color for the parts. --> + <attr name="segSegGap" format="dimension" /> + <attr name="segPointGap" format="dimension" /> + </declare-styleable> + + <!-- Used to config the segments of a NotificationProgressDrawable. --> + <!-- @hide internal use only --> + <declare-styleable name="NotificationProgressDrawableSegments"> + <!-- Width of the stroke. --> + <attr name="width" /> + <!-- Default color of the stroke. --> + <attr name="color" /> + <!-- Length of a dash in the stroke for the dashed segments. --> + <attr name="dashWidth" /> + <!-- Gap between dashes in the stroke for the dashed segments. --> + <attr name="dashGap" /> + </declare-styleable> + + <!-- Used to config the points of a NotificationProgressDrawable. --> + <!-- @hide internal use only --> + <declare-styleable name="NotificationProgressDrawablePoints"> + <!-- Radius (1/2 size) of the point rect. --> + <attr name="radius" /> + <!-- Inset of the point icon or rect. --> + <attr name="inset" /> + <!-- Corner radius of the point rect. --> + <attr name="cornerRadius" format="dimension" /> + <!-- Default color of the point rect. --> + <attr name="color" /> + </declare-styleable> + <!-- ========================== --> <!-- Animation class attributes --> <!-- ========================== --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 6683dc044c9a..b92aa2f355ed 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -304,6 +304,9 @@ <!-- The top margin before the notification progress bar. --> <dimen name="notification_progress_margin_top">8dp</dimen> + <!-- The horizontal margin before and after the notification progress bar. --> + <dimen name="notification_progress_margin_horizontal">2dp</dimen> + <!-- height of the notification header --> <dimen name="notification_header_height">56dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c50c336f8fd8..b7c876545f05 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -224,6 +224,8 @@ <java-symbol type="id" name="status_bar_latest_event_content" /> <java-symbol type="id" name="notification_main_column" /> <java-symbol type="id" name="notification_headerless_view_column" /> + <java-symbol type="id" name="notification_progress_start_icon" /> + <java-symbol type="id" name="notification_progress_end_icon" /> <java-symbol type="id" name="sms_short_code_confirm_message" /> <java-symbol type="id" name="sms_short_code_detail_layout" /> <java-symbol type="id" name="sms_short_code_detail_message" /> @@ -2390,6 +2392,7 @@ <java-symbol type="layout" name="notification_template_material_media" /> <java-symbol type="layout" name="notification_template_material_big_media" /> <java-symbol type="layout" name="notification_template_material_big_text" /> + <java-symbol type="layout" name="notification_template_material_progress" /> <java-symbol type="layout" name="notification_template_header" /> <java-symbol type="layout" name="notification_material_media_action" /> <java-symbol type="color" name="notification_progress_background_color" /> @@ -3205,6 +3208,7 @@ <java-symbol type="dimen" name="notification_heading_margin_end" /> <java-symbol type="dimen" name="notification_content_margin_top" /> <java-symbol type="dimen" name="notification_content_margin" /> + <java-symbol type="dimen" name="notification_progress_margin_horizontal" /> <java-symbol type="dimen" name="notification_header_background_height" /> <java-symbol type="dimen" name="notification_header_touchable_height" /> <java-symbol type="dimen" name="notification_header_expand_icon_size" /> @@ -4076,6 +4080,7 @@ <java-symbol type="dimen" name="text_size_body_2_material" /> <java-symbol type="dimen" name="notification_icon_circle_size" /> <java-symbol type="drawable" name="notification_icon_circle" /> + <java-symbol type="drawable" name="notification_progress_icon_background" /> <java-symbol type="dimen" name="messaging_avatar_size" /> <java-symbol type="dimen" name="messaging_group_sending_progress_size" /> <java-symbol type="dimen" name="messaging_image_rounding" /> diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt index b7bccd46cede..381b566018c7 100644 --- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt +++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt @@ -46,45 +46,43 @@ class BackTouchTrackerTest { val linearTracker = linearTouchTracker() linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) var touchX = 10f - val velocityX = 0f - val velocityY = 0f // Pre-commit - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // Post-commit touchX += 100f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // Cancel touchX -= 10f linearTracker.setTriggerBack(false) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Cancel more touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Restarted, but pre-commit val restartX = touchX touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE) // continue restart within pre-commit touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE) // Restarted, post-commit touchX += 10f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) } @@ -93,46 +91,44 @@ class BackTouchTrackerTest { val linearTracker = linearTouchTracker() linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge - val velocityX = 0f - val velocityY = 0f val target = MAX_DISTANCE // Pre-commit - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) // Post-commit touchX -= 100f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) // Cancel touchX += 10f linearTracker.setTriggerBack(false) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Cancel more touchX += 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // Restarted, but pre-commit val restartX = touchX touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((restartX - touchX) / target) // continue restart within pre-commit touchX -= 10f - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((restartX - touchX) / target) // Restarted, post-commit touchX -= 10f linearTracker.setTriggerBack(true) - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) } @@ -141,28 +137,26 @@ class BackTouchTrackerTest { val nonLinearTracker = nonLinearTouchTracker() nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) var touchX = 10f - val velocityX = 0f - val velocityY = 0f val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR // Pre-commit: linear progress - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // Post-commit: still linear progress touchX += 100f nonLinearTracker.setTriggerBack(true) - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // still linear progress touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) // non linear progress touchX += 10 - nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.update(touchX, 0f) val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress) @@ -178,7 +172,7 @@ class BackTouchTrackerTest { val velocityY = 0f // assert that progress is increased when increasing touchX - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) // assert that progress is reset to 0 when start location is updated @@ -187,13 +181,13 @@ class BackTouchTrackerTest { // assert that progress remains 0 when touchX is decreased touchX -= 50 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // assert that progress uses new minimal touchX for progress calculation val newInitialTouchX = touchX touchX += 100 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE) // assert the same for triggerBack==true @@ -207,11 +201,8 @@ class BackTouchTrackerTest { linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) var touchX = INITIAL_X_RIGHT_EDGE - 100f - val velocityX = 0f - val velocityY = 0f - // assert that progress is increased when decreasing touchX - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE) // assert that progress is reset to 0 when start location is updated @@ -220,13 +211,13 @@ class BackTouchTrackerTest { // assert that progress remains 0 when touchX is increased touchX += 50 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress(0f) // assert that progress uses new maximal touchX for progress calculation val newInitialTouchX = touchX touchX -= 100 - linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.update(touchX, 0f) linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE) // assert the same for triggerBack==true diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 0a4c5e6eb91a..bcbc3025d297 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -685,8 +685,6 @@ public class WindowOnBackInvokedDispatcherTest { /* touchX = */ 0, /* touchY = */ 0, /* progress = */ progress, - /* velocityX = */ 0, - /* velocityY = */ 0, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, /* departingAnimationTarget = */ null); diff --git a/keystore/OWNERS b/keystore/OWNERS index 689177715711..ea783e7f0c06 100644 --- a/keystore/OWNERS +++ b/keystore/OWNERS @@ -1,5 +1,6 @@ # Bug component: 189335 +ascull@google.com drysdale@google.com -eranm@google.com jbires@google.com +sethmo@google.com swillden@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index df80946a99aa..7c5f71647843 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -33,8 +33,6 @@ public interface BackAnimation { * * @param touchX the X touch position of the {@link MotionEvent}. * @param touchY the Y touch position of the {@link MotionEvent}. - * @param velocityX the X velocity computed from the {@link MotionEvent}. - * @param velocityY the Y velocity computed from the {@link MotionEvent}. * @param keyAction the original {@link KeyEvent#getAction()} when the event was dispatched to * the process. This is forwarded separately because the input pipeline may mutate * the {#event} action state later. @@ -43,8 +41,6 @@ public interface BackAnimation { void onBackMotion( float touchX, float touchY, - float velocityX, - float velocityY, int keyAction, @BackEvent.SwipeEdge int swipeEdge); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 5836085e0ddc..b90e6e2fab8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -352,16 +352,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public void onBackMotion( float touchX, float touchY, - float velocityX, - float velocityY, int keyAction, @BackEvent.SwipeEdge int swipeEdge ) { mShellExecutor.execute(() -> onMotionEvent( /* touchX = */ touchX, /* touchY = */ touchY, - /* velocityX = */ velocityX, - /* velocityY = */ velocityY, /* keyAction = */ keyAction, /* swipeEdge = */ swipeEdge)); } @@ -500,14 +496,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public void onMotionEvent( float touchX, float touchY, - float velocityX, - float velocityY, int keyAction, @BackEvent.SwipeEdge int swipeEdge) { BackTouchTracker activeTouchTracker = getActiveTracker(); if (activeTouchTracker != null) { - activeTouchTracker.update(touchX, touchY, velocityX, velocityY); + activeTouchTracker.update(touchX, touchY); } // two gestures are waiting to be processed at the moment, skip any further user touches diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt index e5bfccf0682e..f2c08dc3d1e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt @@ -17,14 +17,12 @@ package com.android.wm.shell.recents import android.app.ActivityManager.RunningTaskInfo -import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.os.IBinder import android.util.ArrayMap import android.view.SurfaceControl -import android.view.WindowManager import android.window.TransitionInfo -import com.android.wm.shell.shared.TransitionUtil import android.window.flags.DesktopModeFlags +import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import dagger.Lazy @@ -40,9 +38,6 @@ class TaskStackTransitionObserver( private val transitions: Lazy<Transitions>, shellInit: ShellInit ) : Transitions.TransitionObserver { - - private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> = - mutableMapOf() private val taskStackTransitionObserverListeners = ArrayMap<TaskStackTransitionObserverListener, Executor>() @@ -63,9 +58,6 @@ class TaskStackTransitionObserver( finishTransaction: SurfaceControl.Transaction ) { if (DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue) { - val taskInfoList = mutableListOf<RunningTaskInfo>() - val transitionTypeList = mutableListOf<Int>() - for (change in info.changes) { if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) { continue @@ -76,59 +68,21 @@ class TaskStackTransitionObserver( continue } - // Filter out changes that we care about - if (change.mode == WindowManager.TRANSIT_OPEN) { - change.taskInfo?.let { taskInfoList.add(it) } - transitionTypeList.add(change.mode) + // Find the first task that is opening, this should be the one at the front after + // the transition + if (TransitionUtil.isOpeningType(change.mode)) { + notifyTaskStackTransitionObserverListeners(taskInfo) + break } } - // Only add the transition to map if it has a change we care about - if (taskInfoList.isNotEmpty()) { - transitionToTransitionChanges.put( - transition, - TransitionChanges(taskInfoList, transitionTypeList) - ) - } } } override fun onTransitionStarting(transition: IBinder) {} - override fun onTransitionMerged(merged: IBinder, playing: IBinder) { - val mergedTransitionChanges = - transitionToTransitionChanges.get(merged) - ?: - // We are adding changes of the merged transition to changes of the playing - // transition so if there is no changes nothing to do. - return - - transitionToTransitionChanges.remove(merged) - val playingTransitionChanges = transitionToTransitionChanges.get(playing) - if (playingTransitionChanges != null) { - playingTransitionChanges.merge(mergedTransitionChanges) - } else { - transitionToTransitionChanges.put(playing, mergedTransitionChanges) - } - } - - override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { - val taskInfoList = - transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList - val typeList = - transitionToTransitionChanges - .getOrDefault(transition, TransitionChanges()) - .transitionTypeList - transitionToTransitionChanges.remove(transition) + override fun onTransitionMerged(merged: IBinder, playing: IBinder) {} - for ((index, taskInfo) in taskInfoList.withIndex()) { - if ( - TransitionUtil.isOpeningType(typeList[index]) && - taskInfo.windowingMode == WINDOWING_MODE_FREEFORM - ) { - notifyTaskStackTransitionObserverListeners(taskInfo) - } - } - } + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {} fun addTaskStackTransitionObserverListener( taskStackTransitionObserverListener: TaskStackTransitionObserverListener, @@ -154,14 +108,4 @@ class TaskStackTransitionObserver( /** Called when a task is moved to front. */ fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {} } - - private data class TransitionChanges( - val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(), - val transitionTypeList: MutableList<Int> = ArrayList(), - ) { - fun merge(transitionChanges: TransitionChanges) { - taskInfoList.addAll(transitionChanges.taskInfoList) - transitionTypeList.addAll(transitionChanges.transitionTypeList) - } - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 227060d15640..dee0b23a42f5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -860,15 +860,9 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void doMotionEvent(int actionDown, int coordinate) { - doMotionEvent(actionDown, coordinate, 0); - } - - private void doMotionEvent(int actionDown, int coordinate, float velocity) { mController.onMotionEvent( /* touchX */ coordinate, /* touchY */ coordinate, - /* velocityX = */ velocity, - /* velocityY = */ velocity, /* keyAction */ actionDown, /* swipeEdge */ BackEvent.EDGE_LEFT); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java index 9b019ddb8362..1da4ef6b5a8b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java @@ -54,8 +54,6 @@ public class BackProgressAnimatorTest { /* touchX = */ touchX, /* touchY = */ 0, /* progress = */ progress, - /* velocityX = */ 0, - /* velocityY = */ 0, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, /* departingAnimationTarget = */ null); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt index 5b5ef6f48789..2235c20d7f21 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt @@ -222,8 +222,6 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() { /* touchX = */ touchX, /* touchY = */ 0f, /* progress = */ progress, - /* velocityX = */ 0f, - /* velocityY = */ 0f, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, /* departingAnimationTarget = */ null diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt index 0e5efa650cc4..afdb68776d04 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt @@ -114,7 +114,7 @@ class TaskStackTransitionObserverTest { @Test @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) - fun taskCreated_fullscreenWindow_listenerNotNotified() { + fun taskCreated_fullscreenWindow_listenerNotified() { val listener = TestListener() val executor = TestShellExecutor() transitionObserver.addTaskStackTransitionObserverListener(listener, executor) @@ -130,9 +130,9 @@ class TaskStackTransitionObserverTest { callOnTransitionFinished() executor.flushAll() - assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0) + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(1) assertThat(listener.taskInfoToBeNotified.windowingMode) - .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED) + .isEqualTo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN) } @Test diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index b763a96e8e8a..c0160705fd47 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -19,6 +19,7 @@ #include <GLES2/gl2ext.h> // TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead. +#include <statslog_hwui.h> #include <surfacetexture/surface_texture_platform.h> #include "AutoBackendTextureRelease.h" @@ -50,6 +51,14 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { setTransform(nullptr); mRenderState.removeContextCallback(this); destroyLayer(); + if (mFirstTimeForDataspace > std::chrono::steady_clock::time_point::min()) { + auto currentTime = std::chrono::steady_clock::now(); + stats_write(stats::TEXTURE_VIEW_EVENT, static_cast<int32_t>(getuid()), + static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>( + currentTime - mFirstTimeForDataspace) + .count()), + mDataspace); + } } void DeferredLayerUpdater::setSurfaceTexture(AutoTextureRelease&& consumer) { @@ -195,6 +204,21 @@ void DeferredLayerUpdater::apply() { updateLayer(forceFilter, layerImage, outTransform, currentCropRect, maxLuminanceNits); } + + if (dataspace != mDataspace || + mFirstTimeForDataspace == std::chrono::steady_clock::time_point::min()) { + auto currentTime = std::chrono::steady_clock::now(); + if (mFirstTimeForDataspace > std::chrono::steady_clock::time_point::min()) { + stats_write(stats::TEXTURE_VIEW_EVENT, static_cast<int32_t>(getuid()), + static_cast<int64_t>( + std::chrono::duration_cast<std::chrono::milliseconds>( + currentTime - mFirstTimeForDataspace) + .count()), + mDataspace); + } + mFirstTimeForDataspace = currentTime; + mDataspace = dataspace; + } } } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index a7f8f6189a8e..3abb47ca92d1 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -16,6 +16,8 @@ #pragma once +#include <EGL/egl.h> +#include <EGL/eglext.h> #include <SkBlendMode.h> #include <SkColorFilter.h> #include <SkImage.h> @@ -24,9 +26,9 @@ #include <android/surface_texture.h> #include <cutils/compiler.h> #include <utils/Errors.h> +#include <utils/Timers.h> -#include <EGL/egl.h> -#include <EGL/eglext.h> +#include <chrono> #include <map> #include <memory> @@ -154,6 +156,9 @@ private: bool mGLContextAttached; bool mUpdateTexImage; int mCurrentSlot = -1; + android_dataspace mDataspace = HAL_DATASPACE_UNKNOWN; + std::chrono::steady_clock::time_point mFirstTimeForDataspace = + std::chrono::steady_clock::time_point::min(); Layer* mLayer; }; diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index c90c44152440..0da8371bc824 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -16,6 +16,9 @@ package android.media; +import static android.media.audio.Flags.FLAG_DOLBY_AC4_LEVEL4_ENCODING_API; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -309,7 +312,7 @@ public final class AudioFormat implements Parcelable { public static final int ENCODING_AAC_ELD = 15; /** Audio data format: AAC xHE compressed */ public static final int ENCODING_AAC_XHE = 16; - /** Audio data format: AC-4 sync frame transport format */ + /** Audio data format: AC-4 (levels 0-3) sync frame transport format */ public static final int ENCODING_AC4 = 17; /** Audio data format: E-AC-3-JOC compressed * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}. @@ -375,6 +378,9 @@ public final class AudioFormat implements Parcelable { public static final int ENCODING_DTS_UHD_P2 = 30; /** Audio data format: Direct Stream Digital */ public static final int ENCODING_DSD = 31; + /** Audio data format: AC-4 level 4 sync frame transport format */ + @FlaggedApi(FLAG_DOLBY_AC4_LEVEL4_ENCODING_API) + public static final int ENCODING_AC4_L4 = 32; /** @hide */ public static String toLogFriendlyEncoding(int enc) { @@ -413,6 +419,8 @@ public final class AudioFormat implements Parcelable { return "ENCODING_AAC_XHE"; case ENCODING_AC4: return "ENCODING_AC4"; + case ENCODING_AC4_L4: + return "ENCODING_AC4_L4"; case ENCODING_E_AC3_JOC: return "ENCODING_E_AC3_JOC"; case ENCODING_DOLBY_MAT: @@ -823,6 +831,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_ELD: case ENCODING_AAC_XHE: case ENCODING_AC4: + case ENCODING_AC4_L4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: case ENCODING_OPUS: @@ -863,6 +872,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_ELD: case ENCODING_AAC_XHE: case ENCODING_AC4: + case ENCODING_AC4_L4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: case ENCODING_OPUS: @@ -908,6 +918,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_ELD: case ENCODING_AAC_XHE: case ENCODING_AC4: + case ENCODING_AC4_L4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: case ENCODING_OPUS: @@ -950,6 +961,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_ELD: case ENCODING_AAC_XHE: case ENCODING_AC4: + case ENCODING_AC4_L4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: case ENCODING_OPUS: @@ -1238,6 +1250,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_ELD: case ENCODING_AAC_XHE: case ENCODING_AC4: + case ENCODING_AC4_L4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: case ENCODING_OPUS: @@ -1468,6 +1481,7 @@ public final class AudioFormat implements Parcelable { ENCODING_AAC_ELD, ENCODING_AAC_XHE, ENCODING_AC4, + ENCODING_AC4_L4, ENCODING_E_AC3_JOC, ENCODING_DOLBY_MAT, ENCODING_OPUS, @@ -1506,6 +1520,7 @@ public final class AudioFormat implements Parcelable { ENCODING_AAC_ELD, ENCODING_AAC_XHE, ENCODING_AC4, + ENCODING_AC4_L4, ENCODING_E_AC3_JOC, ENCODING_DOLBY_MAT, ENCODING_OPUS, @@ -1533,6 +1548,7 @@ public final class AudioFormat implements Parcelable { ENCODING_AAC_LC, ENCODING_DOLBY_TRUEHD, ENCODING_AC4, + ENCODING_AC4_L4, ENCODING_E_AC3_JOC, ENCODING_DOLBY_MAT, ENCODING_MPEGH_BL_L3, @@ -1554,6 +1570,7 @@ public final class AudioFormat implements Parcelable { ENCODING_AAC_LC, ENCODING_DOLBY_TRUEHD, ENCODING_AC4, + ENCODING_AC4_L4, ENCODING_E_AC3_JOC, ENCODING_DOLBY_MAT, ENCODING_MPEGH_BL_L3, @@ -1592,7 +1609,9 @@ public final class AudioFormat implements Parcelable { case ENCODING_DOLBY_TRUEHD: return "Dolby TrueHD"; case ENCODING_AC4: - return "Dolby AC-4"; + return "Dolby AC-4 levels 0-3"; + case ENCODING_AC4_L4: + return "Dolby AC-4 level 4"; case ENCODING_E_AC3_JOC: return "Dolby Atmos in Dolby Digital Plus"; case ENCODING_DOLBY_MAT: diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index ebdfd3e41aa1..a8b863bc67f9 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -542,6 +542,8 @@ public class AudioSystem return "AUDIO_FORMAT_AAC_LATM_HE_V2"; // (AAC_LATM | AAC_SUB_HE_V2) case /* AUDIO_FORMAT_E_AC3_JOC */ 0xA000001: return "AUDIO_FORMAT_E_AC3_JOC"; // (E_AC3 | E_AC3_SUB_JOC) + case /* AUDIO_FORMAT_AC4_L4 */ 0x22000001: + return "AUDIO_FORMAT_AC4_L4"; // (AC4 | AC4_SUB_L4) case /* AUDIO_FORMAT_MAT_1_0 */ 0x24000001: return "AUDIO_FORMAT_MAT_1_0"; // (MAT | MAT_SUB_1_0) case /* AUDIO_FORMAT_MAT_2_0 */ 0x24000002: diff --git a/nfc/Android.bp b/nfc/Android.bp index db3dcb0631dd..7ad8c4c8de41 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -41,6 +41,10 @@ java_sdk_library { "framework-permission-s.stubs.module_lib", "framework-permission.stubs.module_lib", ], + stub_only_libs: [ + // Needed for javadoc references. + "framework-permission-s.stubs.module_lib", + ], static_libs: [ "android.nfc.flags-aconfig-java", "android.permission.flags-aconfig-java", diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 824dd4a5fdaf..d688a1a036d1 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -44,6 +44,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Process; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -100,7 +101,8 @@ public class PackageInstallerActivity extends Activity { private int mActivityResultCode = Activity.RESULT_CANCELED; private int mPendingUserActionReason = -1; - private final boolean mLocalLOGV = false; + private final boolean mLocalLOGV = + TextUtils.equals("userdebug", SystemProperties.get("ro.build.type", "")); PackageManager mPm; AppOpsManager mAppOpsManager; UserManager mUserManager; @@ -143,6 +145,11 @@ public class PackageInstallerActivity extends Activity { private AlertDialog mDialog; private void startInstallConfirm() { + if (mLocalLOGV) { + Log.d(TAG, "startInstallConfirm mAppInfo = " + mAppInfo + + ", existingUpdateOwner = " + getExistingUpdateOwner() + + ", mOriginatingPackage = " + mOriginatingPackage); + } TextView viewToEnable; if (mAppInfo != null) { @@ -183,6 +190,10 @@ public class PackageInstallerActivity extends Activity { try { final String packageName = mPkgInfo.packageName; final InstallSourceInfo sourceInfo = mPm.getInstallSourceInfo(packageName); + if (mLocalLOGV) { + Log.d(TAG, "getExistingUpdateOwner mAppInfo = " + mAppInfo + + ", packageName = " + packageName + ", sourceInfo = " + sourceInfo); + } return sourceInfo.getUpdateOwnerPackageName(); } catch (NameNotFoundException e) { return null; @@ -303,6 +314,12 @@ public class PackageInstallerActivity extends Activity { private void initiateInstall() { final String existingUpdateOwner = getExistingUpdateOwner(); + if (mLocalLOGV) { + Log.d(TAG, "initiateInstall mAppInfo = " + mAppInfo + + ", existingUpdateOwner = " + existingUpdateOwner + + ", mOriginatingPackage = " + mOriginatingPackage + + ", mSessionId = " + mSessionId); + } if (mSessionId == SessionInfo.INVALID_ID && !TextUtils.isEmpty(existingUpdateOwner) && !TextUtils.equals(existingUpdateOwner, mOriginatingPackage)) { @@ -814,15 +831,28 @@ public class PackageInstallerActivity extends Activity { @Override public void onOpChanged(String op, String packageName) { + if (mLocalLOGV) { + Log.d(TAG, "UnknownSourcesListener onOpChanged op = " + op + + ", packageName = " + packageName + + ", mOriginatingPackage = " + mOriginatingPackage); + } if (!mOriginatingPackage.equals(packageName)) { return; } unregister(this); mActiveUnknownSourcesListeners.remove(this); + if (mLocalLOGV) { + Log.d(TAG, "UnknownSourcesListener onOpChanged isDestroyed() = " + + isDestroyed()); + } if (isDestroyed()) { return; } new Handler(Looper.getMainLooper()).postDelayed(() -> { + if (mLocalLOGV) { + Log.d(TAG, "UnknownSourcesListener onOpChanged post isDestroyed()" + + "= " + isDestroyed() + ", getIntent() = " + getIntent()); + } if (!isDestroyed()) { startActivity(getIntent()); // The start flag (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) doesn't @@ -840,6 +870,9 @@ public class PackageInstallerActivity extends Activity { } private void register(UnknownSourcesListener listener) { + if (mLocalLOGV) { + Log.d(TAG, "UnknownSourcesListener register"); + } mAppOpsManager.startWatchingMode( AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, mOriginatingPackage, listener); @@ -847,6 +880,9 @@ public class PackageInstallerActivity extends Activity { } private void unregister(UnknownSourcesListener listener) { + if (mLocalLOGV) { + Log.d(TAG, "UnknownSourcesListener unregister"); + } mAppOpsManager.stopWatchingMode(listener); mActiveUnknownSourcesListeners.remove(listener); } diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java index e6726dcbb17a..03a2101544be 100644 --- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java +++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java @@ -255,4 +255,9 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference { a.recycle(); } } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + public View getExtraWidget() { + return mExtraWidget; + } } diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml deleted file mode 100644 index 8a77d88a7e83..000000000000 --- a/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2024, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- This is the screen that shows the 9 circle unlock widget and instructs - the user how to unlock their device, or make an emergency call. This - is the landscape layout. --> -<com.android.keyguard.KeyguardPatternView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_pattern_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal|bottom" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="2" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <include layout="@layout/keyguard_bouncer_message_area"/> - - <com.android.systemui.bouncer.ui.BouncerMessageView - android:id="@+id/bouncer_message_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - androidprv:layout_constraintBottom_toTopOf="@+id/lockPatternView" - androidprv:layout_constraintTop_toTopOf="parent" - androidprv:layout_constraintVertical_chainStyle="packed" /> - - <include - android:id="@+id/keyguard_selector_fade_container" - layout="@layout/keyguard_eca" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin" - android:layout_marginTop="@dimen/keyguard_eca_top_margin" - android:gravity="center_horizontal" - android:orientation="vertical" - androidprv:layout_constraintBottom_toBottomOf="parent" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/pattern_container" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="3" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/pattern_top_guideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - androidprv:layout_constraintGuide_percent="0" /> - - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPatternView" - android:layout_width="0dp" - android:layout_height="0dp" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal" - androidprv:layout_constraintDimensionRatio="1.0" - androidprv:layout_constraintVertical_bias="1.0" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - androidprv:layout_constraintBottom_toBottomOf="parent" - androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"/> - - </androidx.constraintlayout.widget.ConstraintLayout> -</com.android.keyguard.KeyguardPatternView> diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml deleted file mode 100644 index 4b8b63fe9396..000000000000 --- a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml +++ /dev/null @@ -1,221 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2024, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. --> -<com.android.keyguard.KeyguardSimPinView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/res-auto" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_sim_pin_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal|bottom" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="2" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <include layout="@layout/keyguard_bouncer_message_area"/> - - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_marginBottom="3dp" - android:src="@drawable/ic_lockscreen_sim" - androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - app:tint="@color/background_protected"/> - - <include - android:id="@+id/keyguard_esim_area" - layout="@layout/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"/> - - <com.android.keyguard.AlphaOptimizedRelativeLayout - android:id="@+id/pin_entry_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" - androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"> - - <com.android.keyguard.PasswordTextView - android:id="@+id/simPinEntry" - style="@style/Widget.TextView.Password" - android:layout_width="@dimen/keyguard_security_width" - android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" - android:contentDescription="@string/keyguard_accessibility_sim_pin_area" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </com.android.keyguard.AlphaOptimizedRelativeLayout> - - <include - android:id="@+id/keyguard_selector_fade_container" - layout="@layout/keyguard_eca" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin" - android:layout_marginTop="@dimen/keyguard_eca_top_margin" - android:gravity="center_horizontal" - android:orientation="vertical" - androidprv:layout_constraintBottom_toBottomOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/sim_pin_container" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="3" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <androidx.constraintlayout.helper.widget.Flow - android:id="@+id/flow1" - android:layout_width="0dp" - android:layout_height="0dp" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal" - androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" - androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" - androidprv:flow_horizontalStyle="packed" - androidprv:flow_maxElementsWrap="3" - androidprv:flow_verticalBias="0.5" - androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" - androidprv:flow_verticalStyle="packed" - androidprv:flow_wrapMode="aligned" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - androidprv:layout_constraintTop_toTopOf="parent" - androidprv:layout_constraintBottom_toBottomOf="parent"/> - - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - style="@style/NumPadKey.Delete" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key0" - android:contentDescription="@string/keyboardview_keycode_delete" /> - - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - style="@style/NumPadKey.Enter" - android:layout_width="0dp" - android:layout_height="0dp" - android:contentDescription="@string/keyboardview_keycode_enter" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key2" - androidprv:digit="1" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key3" - androidprv:digit="2" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key4" - androidprv:digit="3" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key5" - androidprv:digit="4" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key6" - androidprv:digit="5" - androidprv:textView="@+id/simPinEntry" /> - - - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key7" - androidprv:digit="6" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key8" - androidprv:digit="7" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key9" - androidprv:digit="8" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/delete_button" - androidprv:digit="9" - androidprv:textView="@+id/simPinEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key_enter" - androidprv:digit="0" - androidprv:textView="@+id/simPinEntry" /> - </androidx.constraintlayout.widget.ConstraintLayout> -</com.android.keyguard.KeyguardSimPinView> diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml deleted file mode 100644 index 9012856c7bb4..000000000000 --- a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml +++ /dev/null @@ -1,223 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2024, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License") -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<!-- This is the SIM PUK view that allows the user to recover their device by entering the - carrier-provided PUK code and entering a new SIM PIN for it. --> -<com.android.keyguard.KeyguardSimPukView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/res-auto" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_sim_puk_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal|bottom" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="2" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <include layout="@layout/keyguard_bouncer_message_area"/> - - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_marginBottom="3dp" - android:src="@drawable/ic_lockscreen_sim" - androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - app:tint="@color/background_protected"/> - - <include - android:id="@+id/keyguard_esim_area" - layout="@layout/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"/> - - <com.android.keyguard.AlphaOptimizedRelativeLayout - android:id="@+id/pin_entry_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" - androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container"> - - <com.android.keyguard.PasswordTextView - android:id="@+id/pukEntry" - style="@style/Widget.TextView.Password" - android:layout_width="@dimen/keyguard_security_width" - android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" - android:contentDescription="@string/keyguard_accessibility_sim_pin_area" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </com.android.keyguard.AlphaOptimizedRelativeLayout> - - <include - android:id="@+id/keyguard_selector_fade_container" - layout="@layout/keyguard_eca" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin" - android:layout_marginTop="@dimen/keyguard_eca_top_margin" - android:gravity="center_horizontal" - android:orientation="vertical" - androidprv:layout_constraintBottom_toBottomOf="parent"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/sim_puk_container" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="3" - android:clipChildren="false" - android:clipToPadding="false" - android:layoutDirection="ltr" - android:orientation="vertical"> - - <androidx.constraintlayout.helper.widget.Flow - android:id="@+id/flow1" - android:layout_width="0dp" - android:layout_height="0dp" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="horizontal" - androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" - androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" - androidprv:flow_horizontalStyle="packed" - androidprv:flow_maxElementsWrap="3" - androidprv:flow_verticalBias="0.5" - androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" - androidprv:flow_verticalStyle="packed" - androidprv:flow_wrapMode="aligned" - androidprv:layout_constraintLeft_toLeftOf="parent" - androidprv:layout_constraintRight_toRightOf="parent" - androidprv:layout_constraintTop_toTopOf="parent" - androidprv:layout_constraintBottom_toBottomOf="parent"/> - - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - style="@style/NumPadKey.Delete" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key0" - android:contentDescription="@string/keyboardview_keycode_delete" /> - - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - style="@style/NumPadKey.Enter" - android:layout_width="0dp" - android:layout_height="0dp" - android:contentDescription="@string/keyboardview_keycode_enter" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key2" - androidprv:digit="1" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key3" - androidprv:digit="2" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key4" - androidprv:digit="3" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key5" - androidprv:digit="4" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key6" - androidprv:digit="5" - androidprv:textView="@+id/pukEntry" /> - - - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key7" - androidprv:digit="6" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key8" - androidprv:digit="7" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key9" - androidprv:digit="8" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/delete_button" - androidprv:digit="9" - androidprv:textView="@+id/pukEntry" /> - - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="0dp" - android:layout_height="0dp" - android:accessibilityTraversalBefore="@id/key_enter" - androidprv:digit="0" - androidprv:textView="@+id/pukEntry" /> - </androidx.constraintlayout.widget.ConstraintLayout> -</com.android.keyguard.KeyguardSimPukView> diff --git a/packages/SystemUI/res/drawable/contextual_edu_all_apps.xml b/packages/SystemUI/res/drawable/contextual_edu_all_apps.xml new file mode 100644 index 000000000000..263e11caf03d --- /dev/null +++ b/packages/SystemUI/res/drawable/contextual_edu_all_apps.xml @@ -0,0 +1,25 @@ +<!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:viewportHeight="32" + android:viewportWidth="32"> + <path + android:fillColor="#001E2D" + android:pathData="M28.6 30.3l-4.1-4a5.2 5.2 0 0 1-2.7 0.7a5.2 5.2 0 0 1-5.3-5.3a5 5 0 0 1 1.5-3.6q1.6-1.6 3.8-1.6a5 5 0 0 1 3.7 1.6a5 5 0 0 1 1.3 5.1q-0.2 0.9-0.6 1.3l4.1 4l-1.7 1.8zm-21.7-3.3a5 5 0 0 1-3.7-1.5a5 5 0 0 1-1.5-3.7a5 5 0 0 1 1.5-3.7a5 5 0 0 1 3.7-1.6a5 5 0 0 1 3.8 1.6a5 5 0 0 1 1.5 3.7a5 5 0 0 1-1.5 3.7a5 5 0 0 1-3.8 1.5zm14.9-2.3q1 0 2-0.8q0.8-1 0.8-2q0-1.2-0.8-2t-2-0.8q-1.3 0-2 0.8q-0.9 0.8-0.9 2q0 1.1 0.9 2q0.8 0.8 2 0.8zm-14.9-12.5a5 5 0 0 1-3.7-1.5q-1.5-1.5-1.5-3.7a5 5 0 0 1 1.5-3.7a5 5 0 0 1 3.7-1.6a5 5 0 0 1 3.8 1.6a5 5 0 0 1 1.5 3.7q0 2.2-1.5 3.7a5 5 0 0 1-3.8 1.5zm14.9 0a5.2 5.2 0 0 1-5.3-5.3a5 5 0 0 1 1.5-3.6q1.6-1.6 3.8-1.6a5 5 0 0 1 3.7 1.6a5 5 0 0 1 1.5 3.7q0 2.2-1.5 3.7a5 5 0 0 1-3.7 1.5z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml b/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml new file mode 100644 index 000000000000..d7000d7f5a5c --- /dev/null +++ b/packages/SystemUI/res/drawable/contextual_edu_dialog_bg.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!-- Setting insetBottom to avoid bottom elevation of dialog being clipped off --> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:insetBottom="@dimen/contextual_edu_dialog_elevation"> + <shape> + <corners android:radius="28dp" /> + <solid android:color="?androidprv:attr/materialColorTertiaryFixed" /> + </shape> +</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/contextual_edu_swipe_back.xml b/packages/SystemUI/res/drawable/contextual_edu_swipe_back.xml new file mode 100644 index 000000000000..8b0142c4638e --- /dev/null +++ b/packages/SystemUI/res/drawable/contextual_edu_swipe_back.xml @@ -0,0 +1,25 @@ +<!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="28dp" + android:height="32dp" + android:viewportHeight="32" + android:viewportWidth="28"> + <path + android:fillColor="#001E2D" + android:pathData="M27.3 9.8h-6.4v-1.8h4a17.4 17.4 0 0 0-5.1-3.1a15.2 15.2 0 0 0-5.8-1.1c-2 0-4 0.4-5.9 1.1a17.6 17.6 0 0 0-5.1 3.1h4v1.8h-6.4v-6.4h1.8l0 2.8a18.6 18.6 0 0 1 11.6-4.2a18.5 18.5 0 0 1 11.6 4.2v-2.8h1.8l0 6.4zm-13.6 20.2c-0.5 0-1-0.1-1.5-0.3s-0.9-0.5-1.3-0.9l-6.8-6.9l0.8-0.9a2.4 2.4 0 0 1 2.5-0.7l2.5 0.7v-11c0-0.3 0.1-0.6 0.3-0.9c0.3-0.3 0.6-0.4 0.9-0.4s0.6 0.1 0.8 0.4c0.2 0.2 0.4 0.5 0.4 0.9v9.4h1.6v-4.3c0-0.3 0.1-0.6 0.3-0.8s0.5-0.4 0.9-0.4s0.6 0.1 0.9 0.4c0.2 0.2 0.4 0.5 0.4 0.8v4.3h1.6v-3c0-0.3 0.1-0.6 0.3-0.8s0.5-0.4 0.9-0.4c0.3 0 0.6 0.1 0.9 0.4c0.3 0.2 0.4 0.5 0.4 0.8v3h1.6v-0.3c0-0.3 0.1-0.6 0.3-0.8s0.5-0.4 0.9-0.4s0.7 0.1 0.9 0.4c0.2 0.2 0.4 0.5 0.4 0.8v5.6c0 1.5-0.5 2.7-1.6 3.8a5.1 5.1 0 0 1-3.8 1.6h-5.6z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/contextual_edu_swipe_up.xml b/packages/SystemUI/res/drawable/contextual_edu_swipe_up.xml new file mode 100644 index 000000000000..294b62658c22 --- /dev/null +++ b/packages/SystemUI/res/drawable/contextual_edu_swipe_up.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:viewportHeight="32" + android:viewportWidth="32"> + <group> + <clip-path android:pathData="M0,0h32v32H0z" /> + <path + android:fillColor="#001E2D" + android:pathData="M21.8 27.1a3.5 3.5 0 0 1-3-0.1l-8.8-4.1l0.5-1.2c0.2-0.4 0.5-0.8 0.8-1c0.4-0.3 0.8-0.5 1.3-0.5l2.6-0.2l-3.8-10.4a1.1 1.1 0 0 1 0-0.9c0.2-0.3 0.4-0.5 0.7-0.6s0.6-0.1 0.9 0s0.5 0.4 0.6 0.7l3.2 8.9l1.5-0.6l-1.5-4.1a1.1 1.1 0 0 1 0-0.9c0.2-0.3 0.4-0.5 0.7-0.6s0.6-0.1 0.9 0s0.5 0.4 0.6 0.7l1.5 4.1l1.5-0.6l-1-2.8a1.1 1.1 0 0 1 0-0.9c0.2-0.3 0.4-0.5 0.7-0.6s0.7-0.1 0.9 0c0.3 0.1 0.5 0.4 0.6 0.7l1 2.8l1.6-0.6l-0.1-0.3a1.1 1.1 0 0 1 0-0.9c0.2-0.3 0.4-0.5 0.7-0.7c0.3-0.1 0.6-0.1 0.9 0.1c0.3 0.1 0.5 0.4 0.6 0.7l1.9 5.2c0.5 1.4 0.5 2.8-0.2 4.1a5.1 5.1 0 0 1-3 2.7l-5.3 1.9zm-13.8-9.4a15.5 15.5 0 0 1-3.4-9.8a17.8 17.8 0 0 1 0.4-3.8l-2.4 2.4l-1.3-1.2l4.5-4.5l4.5 4.5l-1.2 1.2l-2.3-2.3c-0.2 0.6-0.3 1.2-0.4 1.8a14.1 14.1 0 0 0 0.6 6.4a13.5 13.5 0 0 0 2.2 4l-1.3 1.3z" /> + </group> +</vector> diff --git a/packages/SystemUI/res/layout/contextual_edu_dialog.xml b/packages/SystemUI/res/layout/contextual_edu_dialog.xml new file mode 100644 index 000000000000..ee42b2363dd5 --- /dev/null +++ b/packages/SystemUI/res/layout/contextual_edu_dialog.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/contextual_edu_dialog_bg" + android:elevation="@dimen/contextual_edu_dialog_elevation" + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="12dp"> + + <ImageView + android:id="@+id/edu_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@null" + android:importantForAccessibility="no" + android:paddingRight="16dp" /> + + <TextView + android:id="@+id/edu_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@color/transparent" + android:ellipsize="end" + android:fontFamily="google-sans-medium" + android:maxWidth="280dp" + android:textColor="?androidprv:attr/materialColorOnTertiaryFixed" + android:textSize="14sp" + android:textStyle="bold" /> +</LinearLayout> diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 7bd4ca8b940b..17ba2e5c10c4 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -63,10 +63,4 @@ <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> <item name="android:windowLightNavigationBar">false</item> </style> - - <style name="ContextualEduDialog" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> - <!-- To make the dialog wrap to content when the education text is short --> - <item name="windowMinWidthMajor">0%</item> - <item name="windowMinWidthMinor">0%</item> - </style> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 629c94f9a044..1727a5fec0a2 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -2048,5 +2048,6 @@ <dimen name="abc_slice_big_pic_min_height">64dp</dimen> <dimen name="abc_slice_big_pic_max_height">64dp</dimen> - <dimen name="contextual_edu_dialog_bottom_margin">70dp</dimen> + <dimen name="contextual_edu_dialog_bottom_margin">80dp</dimen> + <dimen name="contextual_edu_dialog_elevation">2dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 94b0b5f67934..e1f25f99cc98 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1721,10 +1721,4 @@ <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> <item name="android:windowLightNavigationBar">true</item> </style> - - <style name="ContextualEduDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar"> - <!-- To make the dialog wrap to content when the education text is short --> - <item name="windowMinWidthMajor">0%</item> - <item name="windowMinWidthMinor">0%</item> - </style> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt index 5f421fd19550..ba96f4e56421 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt @@ -17,19 +17,30 @@ package com.android.systemui.communal.ui.binder import android.content.Context +import android.os.Bundle import android.util.SizeF import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.compose.ui.unit.IntSize import androidx.core.view.doOnLayout +import com.android.app.tracing.coroutines.flow.flowOn import com.android.app.tracing.coroutines.launch +import com.android.systemui.Flags.communalWidgetResizing +import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.util.WidgetViewFactory import com.android.systemui.util.kotlin.DisposableHandles +import com.android.systemui.util.kotlin.toDp +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged object CommunalAppWidgetHostViewBinder { private const val TAG = "CommunalAppWidgetHostViewBinder" @@ -37,9 +48,11 @@ object CommunalAppWidgetHostViewBinder { fun bind( context: Context, applicationScope: CoroutineScope, + mainContext: CoroutineContext, + backgroundContext: CoroutineContext, container: FrameLayout, model: CommunalContentModel.WidgetContent.Widget, - size: SizeF, + size: SizeF?, factory: WidgetViewFactory, ): DisposableHandle { val disposables = DisposableHandles() @@ -49,6 +62,22 @@ object CommunalAppWidgetHostViewBinder { val widget = factory.createWidget(context, model, size) waitForLayout(container) container.post { container.setView(widget) } + if (communalWidgetResizing()) { + // Update the app widget size in the background. + launch("$TAG#updateSize", backgroundContext) { + container.sizeFlow().flowOn(mainContext).distinctUntilChanged().collect { + (width, height) -> + widget.updateAppWidgetSize( + /* newOptions = */ Bundle(), + /* minWidth = */ width, + /* minHeight = */ height, + /* maxWidth = */ width, + /* maxHeight = */ height, + /* ignorePadding = */ true, + ) + } + } + } } disposables += DisposableHandle { loadingJob.cancel() } @@ -69,3 +98,13 @@ private fun ViewGroup.setView(view: View) { (view.parent as? ViewGroup)?.removeView(view) addView(view) } + +private fun View.sizeAsDp(): IntSize = IntSize(width.toDp(context), height.toDp(context)) + +private fun View.sizeFlow(): Flow<IntSize> = conflatedCallbackFlow { + if (isLaidOut && !isLayoutRequested) { + trySend(sizeAsDp()) + } + val disposable = onLayoutChanged { trySend(sizeAsDp()) } + awaitClose { disposable.dispose() } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt index 56b769e7bc13..2e12bad744f0 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt @@ -25,13 +25,17 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.ui.binder.CommunalAppWidgetHostViewBinder import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.util.WidgetViewFactory import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.res.R import javax.inject.Inject +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle @@ -39,6 +43,8 @@ class CommunalAppWidgetSection @Inject constructor( @Application private val applicationScope: CoroutineScope, + @Main private val mainContext: CoroutineContext, + @UiBackground private val backgroundContext: CoroutineContext, private val factory: WidgetViewFactory, ) { @@ -76,10 +82,12 @@ constructor( context = context, container = this, model = model, - size = size, + size = if (!communalWidgetResizing()) size else null, factory = factory, applicationScope = applicationScope, - ) + mainContext = mainContext, + backgroundContext = backgroundContext, + ), ) accessibilityDelegate = viewModel.widgetAccessibilityDelegate diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index cafa74faf1a1..07a7c7cba2fd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -43,7 +43,7 @@ constructor( suspend fun createWidget( context: Context, model: CommunalContentModel.WidgetContent.Widget, - size: SizeF, + size: SizeF?, ): CommunalAppWidgetHostView = withContext("$TAG#createWidget", uiBgContext) { val view = @@ -54,14 +54,16 @@ constructor( // Instead of setting the view as the listener directly, we wrap the view in a delegate // which ensures the callbacks always get called on the main thread. appWidgetHost.setListener(model.appWidgetId, listenerFactory.create(view)) - view.updateAppWidgetSize( - /* newOptions = */ Bundle(), - /* minWidth = */ size.width.toInt(), - /* minHeight = */ size.height.toInt(), - /* maxWidth = */ size.width.toInt(), - /* maxHeight = */ size.height.toInt(), - /* ignorePadding = */ true, - ) + if (size != null) { + view.updateAppWidgetSize( + /* newOptions = */ Bundle(), + /* minWidth = */ size.width.toInt(), + /* minHeight = */ size.height.toInt(), + /* maxWidth = */ size.width.toInt(), + /* maxHeight = */ size.height.toInt(), + /* ignorePadding = */ true, + ) + } view } diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt index 287e85ca4358..ca92953dca4a 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt @@ -16,32 +16,40 @@ package com.android.systemui.education.ui.view -import android.app.AlertDialog +import android.app.Dialog import android.content.Context import android.os.Bundle import android.view.Gravity +import android.view.Window import android.view.WindowManager -import android.widget.ToastPresenter +import android.widget.ImageView +import android.widget.TextView import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel import com.android.systemui.res.R class ContextualEduDialog(context: Context, private val model: ContextualEduToastViewModel) : - AlertDialog(context, R.style.ContextualEduDialog) { + Dialog(context) { override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties() setWindowPosition() // title is used for a11y announcement window?.setTitle(context.getString(R.string.contextual_education_dialog_title)) - // TODO: b/369791926 - replace the below toast view with a custom dialog view - val toastView = ToastPresenter.getTextToastView(context, model.message) - setView(toastView) + setContentView(R.layout.contextual_edu_dialog) + setContent() super.onCreate(savedInstanceState) } + private fun setContent() { + findViewById<TextView>(R.id.edu_message)?.let { it.text = model.message } + findViewById<ImageView>(R.id.edu_icon)?.let { it.setImageResource(model.icon) } + } + private fun setUpWindowProperties() { window?.apply { + requestFeature(Window.FEATURE_NO_TITLE) setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG) clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + setBackgroundDrawableResource(android.R.color.transparent) } setCanceledOnTouchOutside(false) } diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt index 632b250512cb..5a02cda8c3f5 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt @@ -21,8 +21,11 @@ sealed class ContextualEduContentViewModel(open val userId: Int) data class ContextualEduNotificationViewModel( val title: String, val message: String, - override val userId: Int + override val userId: Int, ) : ContextualEduContentViewModel(userId) -data class ContextualEduToastViewModel(val message: String, override val userId: Int) : - ContextualEduContentViewModel(userId) +data class ContextualEduToastViewModel( + val message: String, + val icon: Int, + override val userId: Int, +) : ContextualEduContentViewModel(userId) diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt index 55639697674f..7417a7098ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt @@ -71,7 +71,7 @@ constructor( it.userId, ) } else { - ContextualEduToastViewModel(getEduContent(it), it.userId) + ContextualEduToastViewModel(getEduContent(it), getEduIcon(it), it.userId) } } .timeout(timeoutMillis, emitAfterTimeout = null) @@ -118,4 +118,13 @@ constructor( return resources.getString(resourceId) } + + private fun getEduIcon(educationInfo: EducationInfo): Int { + return when (educationInfo.gestureType) { + BACK -> R.drawable.contextual_edu_swipe_back + HOME, + OVERVIEW -> R.drawable.contextual_edu_swipe_up + ALL_APPS -> R.drawable.contextual_edu_all_apps + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 5048a5dfdec0..b3463bdc2949 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -64,7 +64,6 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; -import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.WindowManager; @@ -191,7 +190,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } }; - private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); private final Context mContext; private final UserTracker mUserTracker; private final OverviewProxyService mOverviewProxyService; @@ -1042,10 +1040,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mIsTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe(ev); - // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new - // ACTION_DOWN, in that case we should just reuse the old instance. - mVelocityTracker.clear(); - // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); @@ -1194,28 +1188,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { - mVelocityTracker.addMovement(event); - - final float velocityX; - final float velocityY; - if (event.getAction() == MotionEvent.ACTION_UP) { - // Compute the current velocity is expensive (see computeCurrentVelocity), so we - // are only doing it when the user completes the gesture. - int unitPixelPerSecond = 1000; - int maxVelocity = mViewConfiguration.getScaledMaximumFlingVelocity(); - mVelocityTracker.computeCurrentVelocity(unitPixelPerSecond, maxVelocity); - velocityX = mVelocityTracker.getXVelocity(); - velocityY = mVelocityTracker.getYVelocity(); - } else { - velocityX = Float.NaN; - velocityY = Float.NaN; - } - mBackAnimation.onBackMotion( /* touchX = */ event.getX(), /* touchY = */ event.getY(), - /* velocityX = */ velocityX, - /* velocityY = */ velocityY, /* keyAction = */ event.getActionMasked(), /* swipeEdge = */ mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5003a6af5c4c..7e6dae9118e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -3218,7 +3218,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - protected void resetAllContentAlphas() { + public void resetAllContentAlphas() { mLogger.logResetAllContentAlphas(getEntry()); mPrivateLayout.setAlpha(1f); mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null); @@ -3873,7 +3873,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", mShowingPublicInitialized: " + mShowingPublicInitialized); NotificationContentView showingLayout = getShowingLayout(); pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); + pw.print(", childrenContainerShowing: " + + (!shouldShowPublic() && mIsSummaryWithChildren)); pw.print(", mShowNoBackground: " + mShowNoBackground); + pw.print(", clipBounds: " + getClipBounds()); + pw.println(); if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) { dumpHeights(pw); @@ -3896,6 +3900,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView ? 0 : mChildrenContainer.getTransientViewCount(); if (mIsSummaryWithChildren || transientViewCount > 0) { pw.println(mChildrenContainer.debugString()); + pw.println("Children Container Intrinsic Height: " + + mChildrenContainer.getIntrinsicHeight()); pw.println(); List<ExpandableNotificationRow> notificationChildren = getAttachedChildren(); pw.print("Children: " + notificationChildren.size() + " {"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index b1083888ca7f..cfca8307e703 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -1704,6 +1704,7 @@ public class NotificationChildrenContainer extends ViewGroup + "visibility: " + getVisibility() + ", alpha: " + getAlpha() + ", translationY: " + getTranslationY() + + ", clipBounds: " + getClipBounds() + ", roundableState: " + getRoundableState().debugString() + "}"; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index b171e8768d57..8ae6b7920674 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -113,6 +113,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; +import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; @@ -4372,6 +4373,16 @@ public class NotificationStackScrollLayout } } + private void resetChildAlpha() { + for (int i = 0; i < getChildCount(); i++) { + ExpandableView child = getChildAtIndex(i); + if (child instanceof ExpandableNotificationRow row) { + if (row.isExpandAnimationRunning()) continue; + row.resetAllContentAlphas(); + } + } + } + private void logTransientNotificationRowTraversalCleaned( ExpandableNotificationRow transientView, String reason @@ -4415,6 +4426,9 @@ public class NotificationStackScrollLayout if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(false); } + if (NotificationContentAlphaOptimization.isEnabled()) { + resetChildAlpha(); + } } else { mGroupExpansionManager.collapseGroups(); mExpandHelper.cancelImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index 8206c21fce30..9cd52153eff6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -85,9 +85,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { `when`(telephonyManager.createForSubscriptionId(anyInt())).thenReturn(telephonyManager) `when`(telephonyManager.supplyIccLockPin(anyString())).thenReturn(mock()) simPinView = - LayoutInflater.from(context) - .inflate(R.layout.keyguard_sim_pin_view, null) - .requireViewById(R.id.keyguard_sim_pin_view) as KeyguardSimPinView + LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null) + as KeyguardSimPinView val fakeFeatureFlags = FakeFeatureFlags() val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository()) diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodMockitoTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodMockitoTest.java index dd6d259d5a34..31884b6bfc57 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodMockitoTest.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodMockitoTest.java @@ -16,11 +16,19 @@ package com.android.ravenwoodtest.coretest; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import org.junit.Test; public class RavenwoodMockitoTest { + private static class MockClass { + void foo() { + throw new RuntimeException("Unsupported!!"); + } + } + @Test public void checkMockitoClasses() { // DexMaker should not exist @@ -32,4 +40,11 @@ public class RavenwoodMockitoTest { ClassNotFoundException.class, () -> Class.forName("org.mockito.Matchers")); } + + @Test + public void checkMockitoActuallyWorks() { + var mock = mock(MockClass.class); + doNothing().when(mock).foo(); + mock.foo(); + } } diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt index 49f0b599762f..e67c730df069 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt @@ -127,7 +127,7 @@ class Ravenizer { } } if (includeUnsupportedMockito(allClasses)) { - log.w("Unsupported Mockito detected in $inJar}!") + log.w("Unsupported Mockito detected in $inJar!") } stats.totalProcessTime = log.vTime("$executableName processing $inJar") { diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index ee3bbcaf711d..034127c0420e 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -162,6 +162,13 @@ flag { } flag { + name: "magnification_enlarge_pointer" + namespace: "accessibility" + description: "When fullscreen magnification is enabled, pointer icon is enlarged" + bug: "355734856" +} + +flag { name: "manager_avoid_receiver_timeout" namespace: "accessibility" description: "Register receivers on background handler so they have more time to complete" diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3c95b9a02557..74437cd3c2ef 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8274,7 +8274,16 @@ public class ActivityManagerService extends IActivityManager.Stub setThreadScheduler(proc.getRenderThreadTid(), SCHED_FIFO | SCHED_RESET_ON_FORK, 1); } else { - setThreadPriority(proc.getRenderThreadTid(), THREAD_PRIORITY_TOP_APP_BOOST); + if (Flags.resetOnForkEnabled()) { + if (Process.getThreadScheduler(proc.getRenderThreadTid()) + == Process.SCHED_OTHER) { + Process.setThreadScheduler(proc.getRenderThreadTid(), + Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK, + 0); + } + } + setThreadPriority(proc.getRenderThreadTid(), + THREAD_PRIORITY_TOP_APP_BOOST); } } } else { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 796de1982fe5..4073ab848f81 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -459,12 +459,13 @@ public class OomAdjuster { void setThreadPriority(int tid, int priority) { if (Flags.resetOnForkEnabled()) { - Process.setThreadScheduler(tid, - Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK, - priority); - } else { - Process.setThreadPriority(tid, priority); + if (Process.getThreadScheduler(tid) == Process.SCHED_OTHER) { + Process.setThreadScheduler(tid, + Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK, + 0); + } } + Process.setThreadPriority(tid, priority); } } diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 88268cd19a38..a734e73d213b 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -253,7 +253,11 @@ public final class PlaybackActivityMonitor updateAllowedCapturePolicy(apc, mAllowedCapturePolicies.get(uid)); } } - sEventLogger.enqueue(new NewPlayerEvent(apc)); + var packages = mContext.getPackageManager().getPackagesForUid(apc.getClientUid()); + sEventLogger.enqueue(new NewPlayerEvent( + apc, + packages != null && packages.length > 0 ? packages[0] : null + )); synchronized(mPlayerLock) { mPlayers.put(newPiid, apc); maybeMutePlayerAwaitingConnection(apc); @@ -1402,14 +1406,16 @@ public final class PlaybackActivityMonitor private final int mPlayerIId; private final int mPlayerType; private final int mClientUid; + private final String mClientPackageName; private final int mClientPid; private final AudioAttributes mPlayerAttr; private final int mSessionId; - NewPlayerEvent(AudioPlaybackConfiguration apc) { + NewPlayerEvent(AudioPlaybackConfiguration apc, String packageName) { mPlayerIId = apc.getPlayerInterfaceId(); mPlayerType = apc.getPlayerType(); mClientUid = apc.getClientUid(); + mClientPackageName = packageName; mClientPid = apc.getClientPid(); mPlayerAttr = apc.getAudioAttributes(); mSessionId = apc.getSessionId(); @@ -1418,7 +1424,7 @@ public final class PlaybackActivityMonitor @Override public String eventToString() { return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" - + mClientPid + " type:" + + mClientPid + " package:" + mClientPackageName + " type:" + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) + " attr:" + mPlayerAttr + " session:" + mSessionId); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 8f07bb37e2ff..42a62f098b6a 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -546,7 +546,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mLastBrightnessEvent = new BrightnessEvent(mDisplayId); mTempBrightnessEvent = new BrightnessEvent(mDisplayId); - if (flags.isBatteryStatsEnabledForAllDisplays()) { + if (flags.isBatteryStatsEnabledForAllDisplays() + && isDisplaySupportedForBatteryStats(displayDeviceInfo)) { mBatteryStats = BatteryStatsService.getService(); } else if (mDisplayId == Display.DEFAULT_DISPLAY) { mBatteryStats = BatteryStatsService.getService(); @@ -2772,6 +2773,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private static boolean isDisplaySupportedForBatteryStats(DisplayDeviceInfo displayDeviceInfo) { + switch (displayDeviceInfo.type) { + case Display.TYPE_INTERNAL: + case Display.TYPE_EXTERNAL: + return true; + default: + return false; + } + } + private void dumpBrightnessEvents(PrintWriter pw) { int size = mBrightnessEventRingBuffer.size(); if (size < 1) { diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index b78f8a7d8ee7..0e7d2b631833 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -509,6 +509,21 @@ final class Constants { static final String PROPERTY_STRIP_AUDIO_TV_NO_SYSTEM_AUDIO = "persist.sys.hdmi.property_strip_audio_tv_no_system_audio"; + /** + * Property that decides whether CEC should be disabled on standby when the low energy mode + * option is used. + */ + static final String PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE = + "persist.sys.hdmi.property_was_cec_disabled_on_standby_by_low_energy_mode"; + + /** + * Property that checks if CEC was disabled on standby by low energy mode. With the help of this + * property we avoid re-enabling CEC if the user explicitly disabled it, unrelated to the + * selected energy mode. + */ + static final String PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE = + "persist.sys.hdmi.property_disable_cec_on_standby_in_low_energy_mode"; + static final int RECORDING_TYPE_DIGITAL_RF = 1; static final int RECORDING_TYPE_ANALOGUE_RF = 2; static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3; @@ -644,6 +659,11 @@ final class Constants { }) @interface FeatureFlag {} + /** + * Identifier key for Low energy mode. + */ + static final String KEY_LOW_ENERGY_USE = "low_energy_use"; + private Constants() { /* cannot be instantiated */ } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8e41d18f0953..81be0baefd7a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -22,6 +22,7 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED; import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED; +import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_DISABLED; import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE; import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED; @@ -478,7 +479,8 @@ public class HdmiControlService extends SystemService { @Nullable private HdmiCecController mCecController; - private HdmiCecPowerStatusController mPowerStatusController; + @VisibleForTesting + protected HdmiCecPowerStatusController mPowerStatusController; @Nullable private HdmiEarcController mEarcController; @@ -3814,7 +3816,32 @@ public class HdmiControlService extends SystemService { mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false); if (mCecController != null) { - if (isCecControlEnabled()) { + if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) { + Slog.w(TAG, "Re-enable CEC on wake-up since it was disabled due to low energy " + + " mode."); + getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HDMI_CEC_CONTROL_ENABLED); + setWasCecDisabledOnStandbyByLowEnergyMode(false); + int controlStateChangedReason = -1; + switch (wakeUpAction) { + case WAKE_UP_SCREEN_ON: + controlStateChangedReason = + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP; + break; + case WAKE_UP_BOOT_UP: + controlStateChangedReason = + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START; + break; + default: + Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined."); + return; + + } + // Since CEC is going to be initialized by the setting value update, we must invoke + // the vendor command listeners here with the reason TV woke up. + invokeVendorCommandListenersOnControlStateChanged(true, + controlStateChangedReason); + } else if (isCecControlEnabled()) { int startReason = -1; switch (wakeUpAction) { case WAKE_UP_SCREEN_ON: @@ -3988,6 +4015,14 @@ public class HdmiControlService extends SystemService { if (isAudioSystemDevice() || !isPowerStandby()) { return; } + if (isTvDevice() && getDisableCecOnStandbyByLowEnergyMode() + && mPowerManager.isLowPowerStandbyEnabled()) { + Slog.w(TAG, "Disable CEC on standby due to low power energy mode."); + setWasCecDisabledOnStandbyByLowEnergyMode(true); + getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HDMI_CEC_CONTROL_DISABLED); + } mCecController.enableSystemCecControl(false); mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED); } @@ -5148,4 +5183,34 @@ public class HdmiControlService extends SystemService { protected boolean isHdmiControlEnhancedBehaviorFlagEnabled() { return hdmiControlEnhancedBehavior(); } + + /** + * Reads the property value that decides whether CEC should be disabled on standby when the low + * energy mode option is used. + */ + @VisibleForTesting + protected boolean getDisableCecOnStandbyByLowEnergyMode() { + return SystemProperties.getBoolean( + Constants.PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE, false); + } + + /** + * Reads the property that checks if CEC was disabled on standby by low energy mode. + */ + @VisibleForTesting + protected boolean getWasCecDisabledOnStandbyByLowEnergyMode() { + return SystemProperties.getBoolean( + Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, false); + } + + /** + * Sets the truth value of the property that checks if CEC was disabled on standby by low energy + * mode. + */ + @VisibleForTesting + protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) { + writeStringSystemProperty( + Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, + String.valueOf(value)); + } } diff --git a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java index 7530b3b239b4..5292cbbb9336 100644 --- a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java +++ b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.KEY_LOW_ENERGY_USE; + import android.content.Context; import android.os.PowerManager; @@ -47,6 +49,12 @@ public class PowerManagerWrapper { return new DefaultWakeLockWrapper(mPowerManager.newWakeLock(levelAndFlags, tag)); } + boolean isLowPowerStandbyEnabled() { + PowerManager.LowPowerStandbyPolicy lowPowerStandbyPolicy + = mPowerManager.getLowPowerStandbyPolicy(); + return lowPowerStandbyPolicy.getIdentifier().equals(KEY_LOW_ENERGY_USE); + } + /** * "Default" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Fake" wrapper for * testing - see {@link FakePowerManagerWrapper.FakeWakeLockWrapper}. diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 5d71439e8f46..458b46dab54c 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -592,7 +592,7 @@ public abstract class ApexManager { return apexSessionInfo; } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); - throw new RuntimeException(re); + return null; } } @@ -607,7 +607,7 @@ public abstract class ApexManager { return result; } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); - throw new RuntimeException(re); + return new SparseArray<>(0); } } @@ -619,7 +619,9 @@ public abstract class ApexManager { return apexInfoList; } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); - throw new RuntimeException(re); + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, + "apexd verification failed : " + re.getMessage()); } catch (Exception e) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, @@ -633,7 +635,7 @@ public abstract class ApexManager { return waitForApexService().getStagedApexInfos(params); } catch (RemoteException re) { Slog.w(TAG, "Unable to contact apexservice" + re.getMessage()); - throw new RuntimeException(re); + return new ApexInfo[0]; } catch (Exception e) { Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage()); return new ApexInfo[0]; @@ -646,7 +648,9 @@ public abstract class ApexManager { waitForApexService().markStagedSessionReady(sessionId); } catch (RemoteException re) { Slog.e(TAG, "Unable to contact apexservice", re); - throw new RuntimeException(re); + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, + "Failed to mark apexd session as ready : " + re.getMessage()); } catch (Exception e) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index c581622914fa..897ee4312d22 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1102,6 +1102,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isUpdateOwnershipEnforcementEnabled = mPm.isUpdateOwnershipEnforcementAvailable() && existingUpdateOwnerPackageName != null; + + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " isUpdateOwnershipEnforcementEnabled= " + isUpdateOwnershipEnforcementEnabled + + ", mPm.isUpdateOwnershipEnforcementAvailable= " + + mPm.isUpdateOwnershipEnforcementAvailable() + + ", existingUpdateOwnerPackageName=" + existingUpdateOwnerPackageName + + ", isUpdateOwner= " + isUpdateOwner + ", getInstallerPackageName()= " + + getInstallerPackageName() + ", isInstallerShell= " + isInstallerShell + + ", mInstallerUid=" + mInstallerUid + ", packageName = " + packageName); + } // For an installation that un-archives an app, if the installer doesn't have the // INSTALL_PACKAGES permission, the user should have already been prompted to confirm the // un-archive request. There's no need for another confirmation during the installation. @@ -1115,6 +1126,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { || isInstallUnarchive; if (noUserActionNecessary) { + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " noUserActionNecessary userActionNotTypicallyNeededResponse"); + } return userActionNotTypicallyNeededResponse; } @@ -1124,15 +1139,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { && !isInstallerShell // We don't enforce the update ownership for the managed user and profile. && !isFromManagedUserOrProfile) { + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + "USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER"); + } return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER; } if (isPermissionGranted) { + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " permission userActionNotTypicallyNeededResponse"); + } return userActionNotTypicallyNeededResponse; } if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) { + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " disable USER_ACTION_REQUIRED"); + } // show the installer to account for device policy or unknown sources use cases return USER_ACTION_REQUIRED; } @@ -1141,9 +1168,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { && isUpdateWithoutUserActionPermissionGranted && ((isUpdateOwnershipEnforcementEnabled ? isUpdateOwner : isInstallerOfRecord) || isSelfUpdate)) { + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " USER_ACTION_PENDING_APK_PARSING"); + } return USER_ACTION_PENDING_APK_PARSING; } + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" + + " USER_ACTION_REQUIRED"); + } return USER_ACTION_REQUIRED; } @@ -2714,6 +2749,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED; // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc userActionRequirement = session.computeUserActionRequirement(); + if (Build.IS_USERDEBUG) { + Log.d("updateowner", "PackageInstallerSession checkUserActionRequirement" + + " userActionRequirement= " + userActionRequirement + + ", session.packageName= " + session.getPackageName()); + } session.updateUserActionRequirement(userActionRequirement); if (userActionRequirement == USER_ACTION_REQUIRED || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 611e0d86202a..c8cf938099f4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -340,7 +340,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService static final boolean DEBUG_UPGRADE = false; static final boolean DEBUG_DOMAIN_VERIFICATION = false; static final boolean DEBUG_BACKUP = false; - public static final boolean DEBUG_INSTALL = false; + public static final boolean DEBUG_INSTALL = Build.IS_USERDEBUG; public static final boolean DEBUG_REMOVE = false; static final boolean DEBUG_PACKAGE_INFO = false; static final boolean DEBUG_INTENT_MATCHING = false; diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java index 0d1f36794f49..0ff23eab472a 100644 --- a/services/core/java/com/android/server/uri/UriPermission.java +++ b/services/core/java/com/android/server/uri/UriPermission.java @@ -25,6 +25,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; + import com.google.android.collect.Sets; import java.io.PrintWriter; @@ -82,7 +84,9 @@ final class UriPermission { static final long INVALID_TIME = Long.MIN_VALUE; + @GuardedBy("this") private ArraySet<UriPermissionOwner> mReadOwners; + @GuardedBy("this") private ArraySet<UriPermissionOwner> mWriteOwners; private String stringName; @@ -204,14 +208,16 @@ final class UriPermission { persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; } globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - if (mReadOwners != null && includingOwners) { - ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - for (UriPermissionOwner r : mReadOwners) { - if (r != null) { - r.removeReadPermission(this); + synchronized (this) { + if (mReadOwners != null && includingOwners) { + ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + for (UriPermissionOwner r : mReadOwners) { + if (r != null) { + r.removeReadPermission(this); + } } + mReadOwners = null; } - mReadOwners = null; } } if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { @@ -220,14 +226,16 @@ final class UriPermission { persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; } globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - if (mWriteOwners != null && includingOwners) { - ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - for (UriPermissionOwner r : mWriteOwners) { - if (r != null) { - r.removeWritePermission(this); + synchronized (this) { + if (mWriteOwners != null && includingOwners) { + ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + for (UriPermissionOwner r : mWriteOwners) { + if (r != null) { + r.removeWritePermission(this); + } } + mWriteOwners = null; } - mWriteOwners = null; } } @@ -256,7 +264,7 @@ final class UriPermission { } } - private void addReadOwner(UriPermissionOwner owner) { + private synchronized void addReadOwner(UriPermissionOwner owner) { if (mReadOwners == null) { mReadOwners = Sets.newArraySet(); ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION; @@ -270,7 +278,7 @@ final class UriPermission { /** * Remove given read owner, updating {@Link #modeFlags} as needed. */ - void removeReadOwner(UriPermissionOwner owner) { + synchronized void removeReadOwner(UriPermissionOwner owner) { if (mReadOwners == null || !mReadOwners.remove(owner)) { Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this); return; @@ -282,7 +290,7 @@ final class UriPermission { } } - private void addWriteOwner(UriPermissionOwner owner) { + private synchronized void addWriteOwner(UriPermissionOwner owner) { if (mWriteOwners == null) { mWriteOwners = Sets.newArraySet(); ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; @@ -296,7 +304,7 @@ final class UriPermission { /** * Remove given write owner, updating {@Link #modeFlags} as needed. */ - void removeWriteOwner(UriPermissionOwner owner) { + synchronized void removeWriteOwner(UriPermissionOwner owner) { if (mWriteOwners == null || !mWriteOwners.remove(owner)) { Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this); return; @@ -339,20 +347,22 @@ final class UriPermission { } pw.println(); - if (mReadOwners != null) { - pw.print(prefix); - pw.println("readOwners:"); - for (UriPermissionOwner owner : mReadOwners) { + synchronized (this) { + if (mReadOwners != null) { pw.print(prefix); - pw.println(" * " + owner); + pw.println("readOwners:"); + for (UriPermissionOwner owner : mReadOwners) { + pw.print(prefix); + pw.println(" * " + owner); + } } - } - if (mWriteOwners != null) { - pw.print(prefix); - pw.println("writeOwners:"); - for (UriPermissionOwner owner : mWriteOwners) { + if (mWriteOwners != null) { pw.print(prefix); - pw.println(" * " + owner); + pw.println("writeOwners:"); + for (UriPermissionOwner owner : mWriteOwners) { + pw.print(prefix); + pw.println(" * " + owner); + } } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0b36c7eb5fdf..31f4d081d913 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -880,8 +880,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A }) @interface SplashScreenBehavior { } - // TODO: Have a WindowContainer state for tracking exiting/deferred removal. - boolean mIsExiting; // Force an app transition to be ran in the case the visibility of the app did not change. // We use this for the case of moving a Root Task to the back with multiple activities, and the // top activity enters PIP; the bottom activity's visibility stays the same, but we need to @@ -1227,10 +1225,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn); pw.println(")"); } - if (mStartingData != null || firstWindowDrawn || mIsExiting) { + if (mStartingData != null || firstWindowDrawn) { pw.print(prefix); pw.print("startingData="); pw.print(mStartingData); - pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); - pw.print(" mIsExiting="); pw.println(mIsExiting); + pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); } if (mStartingWindow != null || mStartingData != null || mStartingSurface != null || startingMoved || mVisibleSetFromTransferredStartingWindow) { @@ -4370,21 +4367,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A super.removeImmediately(); } - @Override - void removeIfPossible() { - mIsExiting = false; - removeAllWindowsIfPossible(); - removeImmediately(); - } - - @Override - boolean handleCompleteDeferredRemoval() { - if (mIsExiting) { - removeIfPossible(); - } - return super.handleCompleteDeferredRemoval(); - } - void onRemovedFromDisplay() { if (mRemovingFromDisplay) { return; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 188b368c47c5..1659f7bc6eed 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1753,7 +1753,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static boolean containsChangeFor(WindowContainer wc, ArrayList<ChangeInfo> list) { for (int i = list.size() - 1; i >= 0; --i) { if (list.get(i).mContainer == wc) return true; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index b7fe32713100..87bdfa4f5d75 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -471,6 +471,16 @@ class TransitionController { return false; } + /** Returns {@code true} if the `wc` is a target of a playing transition. */ + boolean isPlayingTarget(@NonNull WindowContainer<?> wc) { + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + if (Transition.containsChangeFor(wc, mPlayingTransitions.get(i).mTargets)) { + return true; + } + } + return false; + } + /** Returns {@code true} if the finishing transition contains `wc`. */ boolean inFinishingTransition(WindowContainer<?> wc) { return mFinishingTransition != null && mFinishingTransition.isInTransition(wc); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ebf645d84f95..f4ad0307d24b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2109,7 +2109,7 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token); // Window will already be removed from token before this post clean-up method is called. if (token.isEmpty() && !token.mPersistOnEmpty) { - token.removeImmediately(); + token.removeIfPossible(); } if (win.mActivityRecord != null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 7e7ca12cd44e..5bde8b5a507c 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -90,6 +90,9 @@ class WindowToken extends WindowContainer<WindowState> { // Is key dispatching paused for this token? boolean paused = false; + /** Whether this container should be removed when it no longer animates. */ + boolean mIsExiting; + /** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */ final boolean mOwnerCanManageAppTokens; @@ -276,6 +279,28 @@ class WindowToken extends WindowContainer<WindowState> { } } + @Override + void removeIfPossible() { + if (mTransitionController.isPlayingTarget(this)) { + // Defer removing this container until the transition is finished. So the removal can + // execute after the finish transaction (see Transition#buildFinishTransaction) which + // may reparent it to original parent. + mIsExiting = true; + return; + } + mIsExiting = false; + removeAllWindowsIfPossible(); + removeImmediately(); + } + + @Override + boolean handleCompleteDeferredRemoval() { + if (mIsExiting) { + removeIfPossible(); + } + return super.handleCompleteDeferredRemoval(); + } + /** * @return The scale for applications running in compatibility mode. Multiply the size in the * application by this scale will be the size in the screen. @@ -725,6 +750,9 @@ class WindowToken extends WindowContainer<WindowState> { pw.print("fixedRotationConfig="); pw.println(mFixedRotationTransformState.mRotatedOverrideConfiguration); } + if (mIsExiting) { + pw.print(prefix); pw.println("isExiting=true"); + } } @Override diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index c70bf8abaef6..01b2d3e34bdc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.description; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -2278,29 +2279,44 @@ public final class DisplayPowerControllerTest { throws Exception { when(mDisplayManagerFlagsMock.isBatteryStatsEnabledForAllDisplays()).thenReturn(false); - verifyNoteScreenState(Display.DEFAULT_DISPLAY, /* expectNote= */ true); + verifyNoteScreenState( + Display.DEFAULT_DISPLAY, Display.TYPE_INTERNAL, /* expectNote= */ true); } @Test public void testBatteryStatNotes_enabledOnDefaultDisplayWhenEnabledOnOthers() throws Exception { when(mDisplayManagerFlagsMock.isBatteryStatsEnabledForAllDisplays()).thenReturn(true); - verifyNoteScreenState(Display.DEFAULT_DISPLAY, /* expectNote= */ true); + verifyNoteScreenState( + Display.DEFAULT_DISPLAY, Display.TYPE_INTERNAL, /* expectNote= */ true); } @Test - public void testBatteryStatNotes_flagGuardedOnNonDefaultDisplays() throws Exception { + public void testBatteryStatNotes_flagOff_disabledForNonDefaultDisplays() throws Exception { when(mDisplayManagerFlagsMock.isBatteryStatsEnabledForAllDisplays()).thenReturn(false); - verifyNoteScreenState(/* displayId= */ 2, /* expectNote= */ false); + verifyNoteScreenState(/* displayId= */ 2, Display.TYPE_INTERNAL, /* expectNote= */ false); + } + @Test + public void testBatteryStatNotes_enabledOnlyOnInternalOrExternalDisplays() throws Exception { when(mDisplayManagerFlagsMock.isBatteryStatsEnabledForAllDisplays()).thenReturn(true); - verifyNoteScreenState(/* displayId= */ 2, /* expectNote= */ true); + for (int displayType = 0; displayType < Display.TYPE_MAX; displayType++) { + boolean expectNote = + (displayType == Display.TYPE_INTERNAL) + || (displayType == Display.TYPE_EXTERNAL); + verifyNoteScreenState(/* displayId= */ 2, displayType, expectNote); + } } - private void verifyNoteScreenState(int displayId, boolean expectNote) throws Exception { - mHolder = createDisplayPowerController(displayId, UNIQUE_ID); + private void verifyNoteScreenState(int displayId, int displayDeviceType, boolean expectNote) + throws Exception { + clearInvocations(mMockBatteryStats); + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.type = displayDeviceType; + deviceInfo.uniqueId = UNIQUE_ID; + mHolder = createDisplayPowerController(displayId, deviceInfo); DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); @@ -2308,14 +2324,22 @@ public final class DisplayPowerControllerTest { mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState + final String baseErrorMessage = + String.format("[display id=%d type=%d]", displayId, displayDeviceType); + final String errorMessage; if (expectNote) { - verify(mMockBatteryStats) + errorMessage = "Expected battery stats: " + baseErrorMessage; + verify(mMockBatteryStats, description(errorMessage)) .noteScreenState( displayId, Display.STATE_ON, Display.STATE_REASON_DEFAULT_POLICY); - verify(mMockBatteryStats).noteScreenBrightness(eq(displayId), anyInt()); + verify(mMockBatteryStats, description(errorMessage)) + .noteScreenBrightness(eq(displayId), anyInt()); } else { - verify(mMockBatteryStats, never()).noteScreenState(anyInt(), anyInt(), anyInt()); - verify(mMockBatteryStats, never()).noteScreenBrightness(anyInt(), anyInt()); + errorMessage = "Expected no battery stats: " + baseErrorMessage; + verify(mMockBatteryStats, never().description(errorMessage)) + .noteScreenState(anyInt(), anyInt(), anyInt()); + verify(mMockBatteryStats, never().description(errorMessage)) + .noteScreenBrightness(anyInt(), anyInt()); } } @@ -2350,17 +2374,16 @@ public final class DisplayPowerControllerTest { private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, boolean isEnabled) { - - setUpDisplay(displayId, uniqueId, logicalDisplayMock, displayDeviceMock, + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; + setUpDisplay(displayId, deviceInfo, logicalDisplayMock, displayDeviceMock, displayDeviceConfigMock, isEnabled, "display_name"); } - private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, - DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, - boolean isEnabled, String displayName) { + private void setUpDisplay(int displayId, DisplayDeviceInfo deviceInfo, + LogicalDisplay logicalDisplayMock, DisplayDevice displayDeviceMock, + DisplayDeviceConfig displayDeviceConfigMock, boolean isEnabled, String displayName) { DisplayInfo info = new DisplayInfo(); - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - deviceInfo.uniqueId = uniqueId; when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); @@ -2368,7 +2391,7 @@ public final class DisplayPowerControllerTest { when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(displayDeviceMock.getUniqueId()).thenReturn(deviceInfo.uniqueId); when(displayDeviceMock.getNameLocked()).thenReturn(displayName); when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); when(displayDeviceConfigMock.getProximitySensor()).thenReturn( @@ -2415,6 +2438,12 @@ public final class DisplayPowerControllerTest { hysteresisLevels); } + private DisplayPowerControllerHolder createDisplayPowerController( + int displayId, DisplayDeviceInfo info) { + return createDisplayPowerController( + displayId, info, /* isEnabled= */ true, /* isAutoBrightnessAvailable= */ true); + } + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId) { return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); @@ -2428,6 +2457,14 @@ public final class DisplayPowerControllerTest { private DisplayPowerControllerHolder createDisplayPowerController(int displayId, String uniqueId, boolean isEnabled, boolean isAutoBrightnessAvailable) { + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + deviceInfo.uniqueId = uniqueId; + return createDisplayPowerController( + displayId, deviceInfo, isEnabled, isAutoBrightnessAvailable); + } + + private DisplayPowerControllerHolder createDisplayPowerController(int displayId, + DisplayDeviceInfo deviceInfo, boolean isEnabled, boolean isAutoBrightnessAvailable) { final DisplayPowerState displayPowerState = mock(DisplayPowerState.class); final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); final AutomaticBrightnessController automaticBrightnessController = @@ -2469,7 +2506,7 @@ public final class DisplayPowerControllerTest { when(config.getScreenBrightnessHysteresis()).thenReturn(hysteresisLevels); when(config.getScreenBrightnessIdleHysteresis()).thenReturn(hysteresisLevels); - setUpDisplay(displayId, uniqueId, display, device, config, isEnabled); + setUpDisplay(displayId, deviceInfo, display, device, config, isEnabled, "display_name"); when(config.isAutoBrightnessAvailable()).thenReturn(isAutoBrightnessAvailable); final DisplayPowerController dpc = new DisplayPowerController( diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index d08cdc718a82..769f071e3ddc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -265,19 +265,19 @@ public class ApexManagerTest { } @Test - public void testSubmitStagedSession_throwRunTimeException() throws RemoteException { + public void testSubmitStagedSession_throwPackageManagerExceptionOnRemoteException() + throws RemoteException { doThrow(RemoteException.class).when(mApexService).submitStagedSession(any(), any()); - assertThrows(RuntimeException.class, + assertThrows(PackageManagerException.class, () -> mApexManager.submitStagedSession(testParamsWithChildren())); } @Test - public void testGetStagedApexInfos_throwRunTimeException() throws RemoteException { + public void testGetStagedApexInfos_returnsEmptyOnRemoteException() throws RemoteException { doThrow(RemoteException.class).when(mApexService).getStagedApexInfos(any()); - assertThrows(RuntimeException.class, - () -> mApexManager.getStagedApexInfos(testParamsWithChildren())); + assertThat(mApexManager.getStagedApexInfos(testParamsWithChildren())).hasLength(0); } @Test @@ -298,10 +298,11 @@ public class ApexManagerTest { } @Test - public void testMarkStagedSessionReady_throwRunTimeException() throws RemoteException { + public void testMarkStagedSessionReady_throwPackageManagerExceptionOnRemoteException() + throws RemoteException { doThrow(RemoteException.class).when(mApexService).markStagedSessionReady(anyInt()); - assertThrows(RuntimeException.class, + assertThrows(PackageManagerException.class, () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID)); } diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java index c305fd92cfbb..dc8c1b9c8a10 100644 --- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java @@ -290,6 +290,9 @@ public class VolumeHelperTest { // --------------- Volume Stream APIs --------------- @Test public void setStreamVolume_callsASSetStreamVolumeIndex() throws Exception { + assumeFalse("Skipping setStreamVolume_callsASSetStreamVolumeIndex on automotive", + mIsAutomotive); + int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC); mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); @@ -313,6 +316,9 @@ public class VolumeHelperTest { @Test public void adjustStreamVolume_callsASSetStreamVolumeIndex() throws Exception { + assumeFalse("Skipping adjustStreamVolume_callsASSetStreamVolumeIndex on automotive", + mIsAutomotive); + mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName()); @@ -324,6 +330,9 @@ public class VolumeHelperTest { @Test public void handleVolumeKey_callsASSetStreamVolumeIndex() throws Exception { + assumeFalse("Skipping handleVolumeKey_callsASSetStreamVolumeIndex on automotive", + mIsAutomotive); + final KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP); mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); @@ -339,6 +348,9 @@ public class VolumeHelperTest { @Test public void setVolumeGroupVolumeIndex_callsASSetVolumeIndexForAttributes() throws Exception { + assumeFalse( + "Skipping setVolumeGroupVolumeIndex_callsASSetVolumeIndexForAttributes on " + + "automotive", mIsAutomotive); assumeNotNull(mAudioMusicVolumeGroup); mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); @@ -347,8 +359,8 @@ public class VolumeHelperTest { mContext.getOpPackageName(), /*attributionTag*/null); mTestLooper.dispatchAll(); - verify(mSpyAudioSystem).setVolumeIndexForAttributes( - any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(), + eq(DEVICE_OUT_USB_DEVICE)); } @Test @@ -366,6 +378,7 @@ public class VolumeHelperTest { @Test public void check_getVolumeGroupVolumeIndex() throws Exception { + assumeFalse("Skipping check_getVolumeGroupVolumeIndex on automotive", mIsAutomotive); assumeNotNull(mAudioMusicVolumeGroup); int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC); @@ -374,10 +387,9 @@ public class VolumeHelperTest { newIndex, /*flags=*/0, mContext.getOpPackageName(), /*attributionTag*/null); mTestLooper.dispatchAll(); - assertEquals(mAudioService.getVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId()), - newIndex); - assertEquals(mAudioService.getStreamVolume(STREAM_MUSIC), - newIndex); + assertEquals(newIndex, + mAudioService.getVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId())); + assertEquals(newIndex, mAudioService.getStreamVolume(STREAM_MUSIC)); } @Test @@ -432,6 +444,7 @@ public class VolumeHelperTest { @Test public void flagAbsVolume_onBtDevice_changesVolume() throws Exception { + assumeFalse("Skipping flagAbsVolume_onBtDevice_changesVolume on automotive", mIsAutomotive); mAudioService.setDeviceForStream(STREAM_NOTIFICATION, DEVICE_OUT_BLE_SPEAKER); int newIndex = circularNoMinMaxIncrementVolume(STREAM_NOTIFICATION); @@ -494,7 +507,7 @@ public class VolumeHelperTest { mContext.getOpPackageName()); mTestLooper.dispatchAll(); - assertEquals(mAudioService.getRingerModeInternal(), RINGER_MODE_VIBRATE); + assertEquals(RINGER_MODE_VIBRATE, mAudioService.getRingerModeInternal()); } // --------------------- Permission tests --------------------- @@ -538,6 +551,7 @@ public class VolumeHelperTest { // ----------------- AudioDeviceVolumeManager ----------------- @Test public void setDeviceVolume_checkIndex() { + assumeFalse("Skipping setDeviceVolume_checkIndex on automotive", mIsAutomotive); final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC); final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC); final int midIndex = (minIndex + maxIndex) / 2; @@ -553,21 +567,17 @@ public class VolumeHelperTest { mAudioService.setDeviceVolume(volMin, usbDevice, mContext.getOpPackageName()); mTestLooper.dispatchAll(); - if (!mIsAutomotive) { - // there is a min/max index mismatch in automotive - assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice, - mContext.getOpPackageName()), volMin); - } + // there is a min/max index mismatch in automotive + assertEquals(volMin, mAudioService.getDeviceVolume(volMin, usbDevice, + mContext.getOpPackageName())); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName()); mTestLooper.dispatchAll(); - if (!mIsAutomotive) { - // there is a min/max index mismatch in automotive - assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice, - mContext.getOpPackageName()), volMid); - } + // there is a min/max index mismatch in automotive + assertEquals(volMid, mAudioService.getDeviceVolume(volMid, usbDevice, + mContext.getOpPackageName())); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); } @@ -603,9 +613,8 @@ public class VolumeHelperTest { mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()); mTestLooper.dispatchAll(); - assertEquals( - mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()), - volCur); + assertEquals(volCur, + mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName())); // Stream volume changes verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( STREAM_MUSIC, targetIndex, @@ -618,9 +627,8 @@ public class VolumeHelperTest { mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()); mTestLooper.dispatchAll(); - assertEquals( - mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()), - volIndex4); + assertEquals(volIndex4, + mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName())); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( STREAM_MUSIC, maxIndex, AudioSystem.DEVICE_OUT_BLE_HEADSET); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java index 04f921f495a2..629f9683a547 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java @@ -26,6 +26,7 @@ final class FakePowerManagerWrapper extends PowerManagerWrapper { private boolean mInteractive; private WakeLockWrapper mWakeLock; private boolean mWasWakeLockInstanceCreated = false; + private boolean mIsLowPowerStandbyEnabled = false; FakePowerManagerWrapper(@NonNull Context context) { @@ -60,6 +61,15 @@ final class FakePowerManagerWrapper extends PowerManagerWrapper { } @Override + boolean isLowPowerStandbyEnabled() { + return mIsLowPowerStandbyEnabled; + } + + void setIsLowPowerStandbyEnabled(boolean isLowPowerStandbyEnabled) { + mIsLowPowerStandbyEnabled = isLowPowerStandbyEnabled; + } + + @Override WakeLockWrapper newWakeLock(int levelAndFlags, String tag) { if (mWakeLock == null) { mWakeLock = new FakeWakeLockWrapper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 2d957401e6bd..935c8b8720fb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -70,6 +70,7 @@ import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; @SmallTest @@ -113,6 +114,10 @@ public class HdmiCecLocalDeviceTvTest { private boolean mWokenUp; private boolean mEarcBlocksArc; private List<DeviceEventListener> mDeviceEventListeners = new ArrayList<>(); + private List<VendorCommandListener> mVendorCommandListeners = new ArrayList<>(); + private boolean mDisableCecOnStandbyByLowEnergyMode; + private boolean mWasCecDisabledOnStandbyByLowEnergyMode; + private boolean mUseHdmiCecPowerStatusController; private class DeviceEventListener { private HdmiDeviceInfo mDevice; @@ -132,6 +137,30 @@ public class HdmiCecLocalDeviceTvTest { } } + private class VendorCommandListener { + private boolean mEnabled; + private int mReason; + + VendorCommandListener(boolean enabled, int reason) { + this.mEnabled = enabled; + this.mReason = reason; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VendorCommandListener)) { + return false; + } + VendorCommandListener other = (VendorCommandListener) obj; + return other.mReason == mReason && other.mEnabled == mEnabled; + } + + @Override + public int hashCode() { + return Objects.hash(mEnabled, mReason); + } + } + private FakeAudioFramework mAudioFramework; private AudioManagerWrapper mAudioManager; @@ -169,6 +198,9 @@ public class HdmiCecLocalDeviceTvTest { @Override boolean isPowerStandby() { + if (mUseHdmiCecPowerStatusController) { + return mPowerStatusController.isPowerStatusStandby(); + } return false; } @@ -188,6 +220,13 @@ public class HdmiCecLocalDeviceTvTest { } @Override + boolean invokeVendorCommandListenersOnControlStateChanged( + boolean enabled, int reason) { + mVendorCommandListeners.add(new VendorCommandListener(enabled, reason)); + return true; + } + + @Override protected boolean earcBlocksArcConnection() { return mEarcBlocksArc; } @@ -196,6 +235,21 @@ public class HdmiCecLocalDeviceTvTest { protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { // do nothing } + + @Override + protected boolean getDisableCecOnStandbyByLowEnergyMode() { + return mDisableCecOnStandbyByLowEnergyMode; + } + + @Override + protected boolean getWasCecDisabledOnStandbyByLowEnergyMode() { + return mWasCecDisabledOnStandbyByLowEnergyMode; + } + + @Override + protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) { + mWasCecDisabledOnStandbyByLowEnergyMode = value; + } }; mHdmiControlService.setIoLooper(mMyLooper); @@ -241,6 +295,9 @@ public class HdmiCecLocalDeviceTvTest { mHdmiControlService.getHdmiCecConfig().setIntValue( sad, HdmiControlManager.QUERY_SAD_DISABLED); } + mWasCecDisabledOnStandbyByLowEnergyMode = false; + mDisableCecOnStandbyByLowEnergyMode = false; + mUseHdmiCecPowerStatusController = false; mNativeWrapper.clearResultMessages(); } @@ -2238,6 +2295,92 @@ public class HdmiCecLocalDeviceTvTest { assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).hasSize(1); } + @Test + public void lowEnergyMode_disableCecOnStandby_reEnableOnWakeup() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertTrue(mWasCecDisabledOnStandbyByLowEnergyMode); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + } + + @Test + public void lowEnergyMode_disableCecBeforeStandby_cecStaysDisabledOnWakeup() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + } + + @Test + public void lowEnergyMode_onWakeUp_reEnableCec_invokeVendorCommandListeners() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + VendorCommandListener vendorCommandListenerInvocationWakeup = new VendorCommandListener( + true, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP); + VendorCommandListener vendorCommandListenerInvocationSettingChange = + new VendorCommandListener(true, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertTrue(mWasCecDisabledOnStandbyByLowEnergyMode); + mVendorCommandListeners.clear(); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertThat(mVendorCommandListeners.size()).isEqualTo(2); + assertTrue(mVendorCommandListeners.contains(vendorCommandListenerInvocationWakeup)); + assertTrue(mVendorCommandListeners.contains(vendorCommandListenerInvocationSettingChange)); + } + protected static class MockTvDevice extends HdmiCecLocalDeviceTv { MockTvDevice(HdmiControlService service) { super(service); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 714eb4b3c093..35328a0e1dc0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; @@ -157,7 +158,16 @@ public class WindowTokenTests extends WindowTestsBase { // Verify that the other token window is still around. assertEquals(1, token.getWindowsCount()); + final TransitionController transitionController = token.mTransitionController; + spyOn(transitionController); + doReturn(true).when(transitionController).isPlayingTarget(token); window2.removeImmediately(); + assertTrue(token.mIsExiting); + assertNotNull("Defer removal for playing transition", token.getParent()); + + doReturn(false).when(transitionController).isPlayingTarget(token); + token.handleCompleteDeferredRemoval(); + assertFalse(token.mIsExiting); // Verify that the token is no-longer attached to its parent assertNull(token.getParent()); // Verify that the token windows are no longer attached to it. |