diff options
128 files changed, 2253 insertions, 766 deletions
@@ -30,6 +30,7 @@ per-file */TEST_MAPPING = * # Support bulk translation updates per-file */res*/values*/*.xml = byi@google.com, delphij@google.com +per-file **.bp,**.mk = hansson@google.com per-file *.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION} per-file Android.mk = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION} per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION} diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 79a26597ed24..5de117261085 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -5277,6 +5277,8 @@ public class DeviceIdleController extends SystemService pw.print(" mScreenLocked="); pw.println(mScreenLocked); pw.print(" mNetworkConnected="); pw.println(mNetworkConnected); pw.print(" mCharging="); pw.println(mCharging); + pw.print(" activeEmergencyCall="); + pw.println(mEmergencyCallListener.isEmergencyCallActive()); if (mConstraints.size() != 0) { pw.println(" mConstraints={"); for (int i = 0; i < mConstraints.size(); i++) { diff --git a/core/api/current.txt b/core/api/current.txt index 53dcd75cf59a..ac635137802a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10333,7 +10333,7 @@ package android.content { field public static final int BIND_AUTO_CREATE = 1; // 0x1 field public static final int BIND_DEBUG_UNBIND = 2; // 0x2 field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000 - field public static final long BIND_EXTERNAL_SERVICE_LONG = -9223372036854775808L; // 0x8000000000000000L + field public static final long BIND_EXTERNAL_SERVICE_LONG = 4611686018427387904L; // 0x4000000000000000L field public static final int BIND_IMPORTANT = 64; // 0x40 field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000 field public static final int BIND_NOT_FOREGROUND = 4; // 0x4 @@ -10441,7 +10441,6 @@ package android.content { } public static final class Context.BindServiceFlags { - method public long getValue(); method @NonNull public static android.content.Context.BindServiceFlags of(long); } @@ -39766,6 +39765,7 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender); method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field); method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull String, @NonNull android.service.autofill.Field); + method @NonNull public android.service.autofill.Dataset.Builder setFieldForAllHints(@NonNull android.service.autofill.Field); method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); @@ -40549,7 +40549,7 @@ package android.service.credentials { method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder addCreateEntry(@NonNull android.service.credentials.CreateEntry); method @NonNull public android.service.credentials.BeginCreateCredentialResponse build(); method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setCreateEntries(@NonNull java.util.List<android.service.credentials.CreateEntry>); - method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.RemoteEntry); + method @NonNull @RequiresPermission("android.permission.PROVIDE_REMOTE_CREDENTIALS") public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.RemoteEntry); } public class BeginGetCredentialOption implements android.os.Parcelable { @@ -40598,7 +40598,7 @@ package android.service.credentials { method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setActions(@NonNull java.util.List<android.service.credentials.Action>); method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setAuthenticationActions(@NonNull java.util.List<android.service.credentials.Action>); method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setCredentialEntries(@NonNull java.util.List<android.service.credentials.CredentialEntry>); - method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.RemoteEntry); + method @NonNull @RequiresPermission("android.permission.PROVIDE_REMOTE_CREDENTIALS") public android.service.credentials.BeginGetCredentialResponse.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.RemoteEntry); } public final class CallingAppInfo implements android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 05f31a5a8a86..a8a48b75b155 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -254,7 +254,7 @@ package android { field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; field public static final String POWER_SAVER = "android.permission.POWER_SAVER"; field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"; - field public static final String PROVIDE_HYBRID_CREDENTIAL_SERVICE = "android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE"; + field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS"; field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE"; @@ -3362,7 +3362,7 @@ package android.companion.virtual.sensor { } public static final class VirtualSensorConfig.Builder { - ctor public VirtualSensorConfig.Builder(int, @NonNull String); + ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build(); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 777faa7fe5aa..a9ae225841da 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2629,8 +2629,13 @@ package android.service.autofill { method @Nullable public android.content.IntentSender getAuthentication(); method @Nullable public java.util.ArrayList<java.lang.String> getAutofillDatatypes(); method @Nullable public android.content.ClipData getFieldContent(); + method @Nullable public android.widget.RemoteViews getFieldDialogPresentation(int); method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds(); + method @Nullable public android.service.autofill.InlinePresentation getFieldInlinePresentation(int); + method @Nullable public android.service.autofill.InlinePresentation getFieldInlineTooltipPresentation(int); + method @Nullable public android.widget.RemoteViews getFieldPresentation(int); method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues(); + method @Nullable public android.service.autofill.Dataset.DatasetFieldFilter getFilter(int); method @Nullable public String getId(); method public boolean isEmpty(); } @@ -2639,6 +2644,13 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData); } + public static final class Dataset.DatasetFieldFilter implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public java.util.regex.Pattern getPattern(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.Dataset.DatasetFieldFilter> CREATOR; + } + public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -3419,6 +3431,7 @@ package android.view.autofill { } public final class AutofillManager { + field public static final String ANY_HINT = "any"; field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0 field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1 field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0 diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index ffbdff8c2e3b..401e754abca6 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -17,6 +17,7 @@ package android.companion.virtual.sensor; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -167,7 +168,10 @@ public final class VirtualSensorConfig implements Parcelable { * @param name The name of the sensor. Must be unique among all sensors with the same type * that belong to the same virtual device. */ - public Builder(int type, @NonNull String name) { + public Builder(@IntRange(from = 1) int type, @NonNull String name) { + if (type <= 0) { + throw new IllegalArgumentException("Virtual sensor type must be positive"); + } mType = type; mName = Objects.requireNonNull(name); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a412560d0347..36f7ff53eb76 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -316,10 +316,12 @@ public abstract class Context { BIND_ALLOW_ACTIVITY_STARTS, BIND_INCLUDE_CAPABILITIES, BIND_SHARED_ISOLATED_PROCESS, - // Intentionally not included, because it'd cause sign-extension. + // Intentionally not include BIND_EXTERNAL_SERVICE, because it'd cause sign-extension. // This would allow Android Studio to show a warning, if someone tries to use // BIND_EXTERNAL_SERVICE BindServiceFlags. - BIND_EXTERNAL_SERVICE_LONG + BIND_EXTERNAL_SERVICE_LONG, + // Make sure no flag uses the sign bit (most significant bit) of the long integer, + // to avoid future confusion. }) @Retention(RetentionPolicy.SOURCE) public @interface BindServiceFlagsLongBits {} @@ -338,6 +340,7 @@ public abstract class Context { /** * @return Return flags in 64 bits long integer. + * @hide */ public long getValue() { return mValue; @@ -678,13 +681,11 @@ public abstract class Context { */ public static final int BIND_EXTERNAL_SERVICE = 0x80000000; - /** * Works in the same way as {@link #BIND_EXTERNAL_SERVICE}, but it's defined as a (@code long) * value that is compatible to {@link BindServiceFlags}. */ - public static final long BIND_EXTERNAL_SERVICE_LONG = 0x8000_0000_0000_0000L; - + public static final long BIND_EXTERNAL_SERVICE_LONG = 1L << 62; /** * These bind flags reduce the strength of the binding such that we shouldn't diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index e2af9b03d7f5..b749d695bdc8 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -420,6 +420,7 @@ public final class NfcAdapter { // Guarded by NfcAdapter.class static boolean sIsInitialized = false; static boolean sHasNfcFeature; + static boolean sHasCeFeature; // Final after first constructor, except for // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort @@ -616,11 +617,13 @@ public final class NfcAdapter { PackageManager pm; pm = context.getPackageManager(); sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC); - boolean hasHceFeature = + sHasCeFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF); + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE); /* is this device meant to have NFC */ - if (!sHasNfcFeature && !hasHceFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { Log.v(TAG, "this device does not have NFC support"); throw new UnsupportedOperationException(); } @@ -643,7 +646,7 @@ public final class NfcAdapter { throw new UnsupportedOperationException(); } } - if (hasHceFeature) { + if (sHasCeFeature) { try { sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); } catch (RemoteException e) { @@ -1669,7 +1672,7 @@ public final class NfcAdapter { @SystemApi @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean enable) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1694,10 +1697,13 @@ public final class NfcAdapter { * Checks if the device supports Secure NFC functionality. * * @return True if device supports Secure NFC, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ public boolean isSecureNfcSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1723,11 +1729,14 @@ public final class NfcAdapter { * such as their relative positioning on the device. * * @return Information on the nfc antenna(s) on the device. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ @Nullable public NfcAntennaInfo getNfcAntennaInfo() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1752,12 +1761,15 @@ public final class NfcAdapter { * Checks Secure NFC feature is enabled. * * @return True if Secure NFC is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @throws UnsupportedOperationException if device doesn't support * Secure NFC functionality. {@link #isSecureNfcSupported} */ public boolean isSecureNfcEnabled() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2071,14 +2083,17 @@ public final class NfcAdapter { * always on. * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is * disabled), if false the NFCC will follow completely the Nfc adapter state. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @return void * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean value) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2103,7 +2118,10 @@ public final class NfcAdapter { * Checks NFC controller always on feature is enabled. * * @return True if NFC controller always on is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @@ -2131,13 +2149,16 @@ public final class NfcAdapter { * Checks if the device supports NFC controller always on functionality. * * @return True if device supports NFC controller always on, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index d943bf9ac872..0ef8bb64acaf 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -29,6 +29,7 @@ import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.widget.RemoteViews; @@ -283,24 +284,28 @@ public final class Dataset implements Parcelable { } /** @hide */ - public RemoteViews getFieldPresentation(int index) { + @TestApi + public @Nullable RemoteViews getFieldPresentation(int index) { final RemoteViews customPresentation = mFieldPresentations.get(index); return customPresentation != null ? customPresentation : mPresentation; } /** @hide */ - public RemoteViews getFieldDialogPresentation(int index) { + @TestApi + public @Nullable RemoteViews getFieldDialogPresentation(int index) { final RemoteViews customPresentation = mFieldDialogPresentations.get(index); return customPresentation != null ? customPresentation : mDialogPresentation; } /** @hide */ + @TestApi public @Nullable InlinePresentation getFieldInlinePresentation(int index) { final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index); return inlinePresentation != null ? inlinePresentation : mInlinePresentation; } /** @hide */ + @TestApi public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) { final InlinePresentation inlineTooltipPresentation = mFieldInlineTooltipPresentations.get(index); @@ -309,6 +314,7 @@ public final class Dataset implements Parcelable { } /** @hide */ + @TestApi public @Nullable DatasetFieldFilter getFilter(int index) { return mFieldFilters.get(index); } @@ -389,6 +395,9 @@ public final class Dataset implements Parcelable { if (mAuthentication != null) { builder.append(", hasAuthentication"); } + if (mAutofillDatatypes != null) { + builder.append(", autofillDatatypes=").append(mAutofillDatatypes); + } return builder.append(']').toString(); } @@ -1090,8 +1099,7 @@ public final class Dataset implements Parcelable { * * @return this builder. */ - public @NonNull Dataset.Builder setField( - @NonNull String hint, @NonNull Field field) { + public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) { throwIfDestroyed(); final DatasetFieldFilter filter = field.getDatasetFieldFilter(); @@ -1111,6 +1119,23 @@ public final class Dataset implements Parcelable { } /** + * Adds a field to this Dataset that is relevant to all applicable hints. This is used to + * provide field information when autofill with platform detections is enabled. + * Platform detections are on when receiving a populated list from + * FillRequest#getHints(). + * + * @param field the fill information about the field. + * + * @throws IllegalStateException if {@link #build()} was already called + * or this builder also contains AutofillId information + * + * @return this builder. + */ + public @NonNull Dataset.Builder setFieldForAllHints(@NonNull Field field) { + return setField(AutofillManager.ANY_HINT, field); + } + + /** * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an * {@link InlinePresentation} to visualize it as an inline suggestion. * @@ -1304,7 +1329,7 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList<DatasetFieldFilter> filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); - final ArrayList<String> datatypes = + final ArrayList<String> autofillDatatypes = parcel.createStringArrayList(); final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class); @@ -1341,9 +1366,9 @@ public final class Dataset implements Parcelable { } final int inlinePresentationsSize = inlinePresentations.size(); - if (ids.size() == 0 && datatypes.size() > 0) { - for (int i = 0; i < ids.size(); i++) { - final String datatype = datatypes.get(i); + if (ids.size() == 0 && autofillDatatypes.size() > 0) { + for (int i = 0; i < autofillDatatypes.size(); i++) { + final String datatype = autofillDatatypes.get(i); final AutofillValue value = values.get(i); final RemoteViews fieldPresentation = presentations.get(i); final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); @@ -1393,8 +1418,10 @@ public final class Dataset implements Parcelable { * * @hide */ + @TestApi public static final class DatasetFieldFilter implements Parcelable { + /** @hide */ @Nullable public final Pattern pattern; @@ -1402,6 +1429,10 @@ public final class Dataset implements Parcelable { this.pattern = pattern; } + public @Nullable Pattern getPattern() { + return pattern; + } + @Override public String toString() { if (!sDebug) return super.toString(); @@ -1416,7 +1447,7 @@ public final class Dataset implements Parcelable { } @Override - public void writeToParcel(Parcel parcel, int flags) { + public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeSerializable(pattern); } diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java index eebd31c6fa96..cd53cb6afc71 100644 --- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java @@ -16,8 +16,10 @@ package android.service.credentials; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.os.Parcel; import android.os.Parcelable; @@ -137,7 +139,17 @@ public final class BeginCreateCredentialResponse implements Parcelable { * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the * {@link CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESPONSE} key should be populated * with a {@link android.credentials.CreateCredentialResponse} object. + * + * <p> Note that as a provider service you will only be able to set a remote entry if : + * - Provider service possesses the + * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission. + * - Provider service is configured as the provider that can provide remote entries. + * + * If the above conditions are not met, setting back {@link BeginCreateCredentialResponse} + * on the callback from {@link CredentialProviderService#onBeginCreateCredential} + * will throw a {@link SecurityException}. */ + @RequiresPermission(Manifest.permission.PROVIDE_REMOTE_CREDENTIALS) public @NonNull Builder setRemoteCreateEntry(@Nullable RemoteEntry remoteCreateEntry) { mRemoteCreateEntry = remoteCreateEntry; return this; diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java index 97f5079393a0..e25b6869605d 100644 --- a/core/java/android/service/credentials/BeginGetCredentialResponse.java +++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java @@ -16,8 +16,10 @@ package android.service.credentials; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.os.Parcel; import android.os.Parcelable; @@ -154,7 +156,17 @@ public final class BeginGetCredentialResponse implements Parcelable { * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the * {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_RESPONSE} key should be populated * with a {@link android.credentials.Credential} object. + * + * <p> Note that as a provider service you will only be able to set a remote entry if : + * - Provider service possesses the + * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission. + * - Provider service is configured as the provider that can provide remote entries. + * + * If the above conditions are not met, setting back {@link BeginGetCredentialResponse} + * on the callback from {@link CredentialProviderService#onBeginGetCredential} will + * throw a {@link SecurityException}. */ + @RequiresPermission(Manifest.permission.PROVIDE_REMOTE_CREDENTIALS) public @NonNull Builder setRemoteCredentialEntry(@Nullable RemoteEntry remoteCredentialEntry) { mRemoteCredentialEntry = remoteCredentialEntry; diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index d737f6b6cdc8..e88474d86798 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -18,6 +18,7 @@ package android.service.credentials; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import android.Manifest; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.SdkConstant; @@ -218,6 +219,11 @@ public abstract class CredentialProviderService extends Service { GetCredentialException>() { @Override public void onResult(BeginGetCredentialResponse result) { + // If provider service does not possess the HYBRID permission, this + // check will throw an exception in the provider process. + if (result.getRemoteCredentialEntry() != null) { + enforceRemoteEntryPermission(); + } try { callback.onSuccess(result); } catch (RemoteException e) { @@ -236,6 +242,15 @@ public abstract class CredentialProviderService extends Service { )); return transport; } + private void enforceRemoteEntryPermission() { + String permission = + Manifest.permission.PROVIDE_REMOTE_CREDENTIALS; + getApplicationContext().enforceCallingOrSelfPermission( + permission, + String.format("Provider must have %s, in order to set a " + + "remote entry", permission) + ); + } @Override public ICancellationSignal onBeginCreateCredential(BeginCreateCredentialRequest request, @@ -253,6 +268,11 @@ public abstract class CredentialProviderService extends Service { BeginCreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(BeginCreateCredentialResponse result) { + // If provider service does not possess the HYBRID permission, this + // check will throw an exception in the provider process. + if (result.getRemoteCreateEntry() != null) { + enforceRemoteEntryPermission(); + } try { callback.onSuccess(result); } catch (RemoteException e) { diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index 07ac5977c4e3..b4675e0127de 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -81,7 +81,10 @@ public abstract class DisplayEventReceiver { // GC'd while the native peer of the receiver is using them. private MessageQueue mMessageQueue; + private final VsyncEventData mVsyncEventData = new VsyncEventData(); + private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, + WeakReference<VsyncEventData> vsyncEventData, MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle); private static native long nativeGetDisplayEventReceiverFinalizer(); @FastNative @@ -124,7 +127,9 @@ public abstract class DisplayEventReceiver { } mMessageQueue = looper.getQueue(); - mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, + mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), + new WeakReference<VsyncEventData>(mVsyncEventData), + mMessageQueue, vsyncSource, eventRegistration, layerHandle); mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this, mReceiverPtr); @@ -142,9 +147,6 @@ public abstract class DisplayEventReceiver { } static final class VsyncEventData { - - static final FrameTimeline[] INVALID_FRAME_TIMELINES = - {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)}; // The amount of frame timeline choices. // Must be in sync with VsyncEventData::kFrameTimelinesLength in // frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime @@ -152,6 +154,10 @@ public abstract class DisplayEventReceiver { static final int FRAME_TIMELINES_LENGTH = 7; public static class FrameTimeline { + FrameTimeline() {} + + // Called from native code. + @SuppressWarnings("unused") FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) { this.vsyncId = vsyncId; this.expectedPresentationTime = expectedPresentationTime; @@ -160,14 +166,14 @@ public abstract class DisplayEventReceiver { // The frame timeline vsync id, used to correlate a frame // produced by HWUI with the timeline data stored in Surface Flinger. - public final long vsyncId; + public long vsyncId = FrameInfo.INVALID_VSYNC_ID; // The frame timestamp for when the frame is expected to be presented. - public final long expectedPresentationTime; + public long expectedPresentationTime = Long.MAX_VALUE; // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is // allotted for the frame to be completed. - public final long deadline; + public long deadline = Long.MAX_VALUE; } /** @@ -175,11 +181,18 @@ public abstract class DisplayEventReceiver { * {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily * delayed by the app. */ - public final long frameInterval; + public long frameInterval = -1; public final FrameTimeline[] frameTimelines; - public final int preferredFrameTimelineIndex; + public int preferredFrameTimelineIndex = 0; + + VsyncEventData() { + frameTimelines = new FrameTimeline[FRAME_TIMELINES_LENGTH]; + for (int i = 0; i < frameTimelines.length; i++) { + frameTimelines[i] = new FrameTimeline(); + } + } // Called from native code. @SuppressWarnings("unused") @@ -190,12 +203,6 @@ public abstract class DisplayEventReceiver { this.frameInterval = frameInterval; } - VsyncEventData() { - this.frameInterval = -1; - this.frameTimelines = INVALID_FRAME_TIMELINES; - this.preferredFrameTimelineIndex = 0; - } - public FrameTimeline preferredFrameTimeline() { return frameTimelines[preferredFrameTimelineIndex]; } @@ -299,9 +306,8 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") - private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, - VsyncEventData vsyncEventData) { - onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData); + private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { + onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData); } // Called from native code. diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index a208cb3de8aa..21fe87f42e1b 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -823,6 +823,11 @@ public abstract class Window { /** @hide */ public final void destroy() { mDestroyed = true; + onDestroy(); + } + + /** @hide */ + protected void onDestroy() { } /** @hide */ diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index aef0e651ff8d..ab9f492694f5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -301,6 +301,14 @@ public final class AutofillManager { public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT = "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT"; + /** + * Autofill Hint to indicate that it can match any field. + * + * @hide + */ + @TestApi + public static final String ANY_HINT = "any"; + private static final String SESSION_ID_TAG = "android:sessionId"; private static final String STATE_TAG = "android:state"; private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index c41b8223d181..9b9e010039a6 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -295,6 +295,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mClosingActionMenu; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; + private int mAudioMode = AudioManager.MODE_NORMAL; private MediaController mMediaController; private AudioManager mAudioManager; @@ -317,6 +318,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } }; + private AudioManager.OnModeChangedListener mOnModeChangedListener; + private Transition mEnterTransition = null; private Transition mReturnTransition = USE_DEFAULT_TRANSITION; private Transition mExitTransition = null; @@ -1944,7 +1947,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_VOLUME_MUTE: { // If we have a session and no active phone call send it the volume command, // otherwise use the suggested stream. - if (mMediaController != null && !isActivePhoneCallKnown()) { + if (mMediaController != null && !isActivePhoneCallOngoing()) { getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event, mMediaController.getSessionToken()); } else { @@ -1995,16 +1998,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } - private boolean isActivePhoneCallKnown() { - boolean isActivePhoneCallKnown = false; - AudioManager audioManager = - (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); - int audioManagerMode = audioManager.getMode(); - if (audioManagerMode == AudioManager.MODE_IN_CALL - || audioManagerMode == AudioManager.MODE_IN_COMMUNICATION) { - isActivePhoneCallKnown = true; - } - return isActivePhoneCallKnown; + private boolean isActivePhoneCallOngoing() { + return mAudioMode == AudioManager.MODE_IN_CALL + || mAudioMode == AudioManager.MODE_IN_COMMUNICATION; } private KeyguardManager getKeyguardManager() { @@ -2331,6 +2327,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + @Override + protected void onDestroy() { + if (mOnModeChangedListener != null) { + getAudioManager().removeOnModeChangedListener(mOnModeChangedListener); + mOnModeChangedListener = null; + } + } + private class PanelMenuPresenterCallback implements MenuPresenter.Callback { @Override public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { @@ -3229,6 +3233,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setMediaController(MediaController controller) { mMediaController = controller; + if (controller != null && mOnModeChangedListener == null) { + mAudioMode = getAudioManager().getMode(); + mOnModeChangedListener = mode -> mAudioMode = mode; + getAudioManager().addOnModeChangedListener(getContext().getMainExecutor(), + mOnModeChangedListener); + } else if (mOnModeChangedListener != null) { + getAudioManager().removeOnModeChangedListener(mOnModeChangedListener); + mOnModeChangedListener = null; + } } @Override diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java index f2b0544bcfad..abe6c7c079c2 100644 --- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java @@ -72,11 +72,13 @@ public class BaseProtoLogImpl { private static final String TAG = "ProtoLog"; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; static final String PROTOLOG_VERSION = "1.0.0"; + private static final int DEFAULT_PER_CHUNK_SIZE = 0; private final File mLogFile; private final String mViewerConfigFilename; private final TraceBuffer mBuffer; protected final ProtoLogViewerConfigReader mViewerConfig; + private final int mPerChunkSize; private boolean mProtoLogEnabled; private boolean mProtoLogEnabledLockFree; @@ -160,7 +162,7 @@ public class BaseProtoLogImpl { return; } try { - ProtoOutputStream os = new ProtoOutputStream(); + ProtoOutputStream os = new ProtoOutputStream(mPerChunkSize); long token = os.start(LOG); os.write(MESSAGE_HASH, messageHash); os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); @@ -219,10 +221,16 @@ public class BaseProtoLogImpl { public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) { + this(file, viewerConfigFilename, bufferCapacity, viewerConfig, DEFAULT_PER_CHUNK_SIZE); + } + + public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity, + ProtoLogViewerConfigReader viewerConfig, int perChunkSize) { mLogFile = file; mBuffer = new TraceBuffer(bufferCapacity); mViewerConfigFilename = viewerConfigFilename; mViewerConfig = viewerConfig; + mPerChunkSize = perChunkSize; } /** @@ -368,7 +376,7 @@ public class BaseProtoLogImpl { try { long offset = (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000)); - ProtoOutputStream proto = new ProtoOutputStream(); + ProtoOutputStream proto = new ProtoOutputStream(mPerChunkSize); proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); proto.write(VERSION, PROTOLOG_VERSION); proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset); diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 353c6c083d9d..527cfddf6d8e 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -30,6 +30,7 @@ public class ProtoLogImpl extends BaseProtoLogImpl { private static final int BUFFER_CAPACITY = 1024 * 1024; private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope"; private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz"; + private static final int PER_CHUNK_SIZE = 1024; private static ProtoLogImpl sServiceInstance = null; @@ -94,7 +95,10 @@ public class ProtoLogImpl extends BaseProtoLogImpl { public static synchronized ProtoLogImpl getSingleInstance() { if (sServiceInstance == null) { sServiceInstance = new ProtoLogImpl( - new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader()); + new File(LOG_FILENAME) + , BUFFER_CAPACITY + , new ProtoLogViewerConfigReader() + , PER_CHUNK_SIZE); } return sServiceInstance; } @@ -105,8 +109,8 @@ public class ProtoLogImpl extends BaseProtoLogImpl { } public ProtoLogImpl(File logFile, int bufferCapacity, - ProtoLogViewerConfigReader viewConfigReader) { - super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader); - } + ProtoLogViewerConfigReader viewConfigReader, int perChunkSize) { + super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader, perChunkSize); + } } diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index b09a9c3a505d..739055e040af 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -50,12 +50,22 @@ static struct { struct { jclass clazz; + jmethodID init; + + jfieldID vsyncId; + jfieldID expectedPresentationTime; + jfieldID deadline; } frameTimelineClassInfo; struct { jclass clazz; + jmethodID init; + + jfieldID frameInterval; + jfieldID preferredFrameTimelineIndex; + jfieldID frameTimelines; } vsyncEventDataClassInfo; } gDisplayEventReceiverClassInfo; @@ -63,7 +73,7 @@ static struct { class NativeDisplayEventReceiver : public DisplayEventDispatcher { public: - NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, jobject vsyncEventDataWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, jint eventRegistration, jlong layerHandle); @@ -74,6 +84,7 @@ protected: private: jobject mReceiverWeakGlobal; + jobject mVsyncEventDataWeakGlobal; sp<MessageQueue> mMessageQueue; void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, @@ -87,6 +98,7 @@ private: }; NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + jobject vsyncEventDataWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, jint eventRegistration, jlong layerHandle) @@ -98,6 +110,7 @@ NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject rece reinterpret_cast<IBinder*>(layerHandle)) : nullptr), mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), + mVsyncEventDataWeakGlobal(env->NewGlobalRef(vsyncEventDataWeak)), mMessageQueue(messageQueue) { ALOGV("receiver %p ~ Initializing display event receiver.", this); } @@ -144,12 +157,39 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla JNIEnv* env = AndroidRuntime::getJNIEnv(); ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal)); - if (receiverObj.get()) { + ScopedLocalRef<jobject> vsyncEventDataObj(env, GetReferent(env, mVsyncEventDataWeakGlobal)); + if (receiverObj.get() && vsyncEventDataObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); - jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData); + env->SetIntField(vsyncEventDataObj.get(), + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo + .preferredFrameTimelineIndex, + vsyncEventData.preferredFrameTimelineIndex); + env->SetLongField(vsyncEventDataObj.get(), + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval, + vsyncEventData.frameInterval); + + jobjectArray frameTimelinesObj = reinterpret_cast<jobjectArray>( + env->GetObjectField(vsyncEventDataObj.get(), + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo + .frameTimelines)); + for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) { + VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i]; + jobject frameTimelineObj = env->GetObjectArrayElement(frameTimelinesObj, i); + env->SetLongField(frameTimelineObj, + gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId, + frameTimeline.vsyncId); + env->SetLongField(frameTimelineObj, + gDisplayEventReceiverClassInfo.frameTimelineClassInfo + .expectedPresentationTime, + frameTimeline.expectedPresentationTime); + env->SetLongField(frameTimelineObj, + gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline, + frameTimeline.deadlineTimestamp); + } + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, - timestamp, displayId.value, count, javaVsyncEventData); + timestamp, displayId.value, count); ALOGV("receiver %p ~ Returned from vsync handler.", this); } @@ -217,8 +257,9 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides( mMessageQueue->raiseAndClearException(env, "dispatchModeChanged"); } -static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, - jint vsyncSource, jint eventRegistration, jlong layerHandle) { +static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak, + jobject messageQueueObj, jint vsyncSource, jint eventRegistration, + jlong layerHandle) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); @@ -226,8 +267,8 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject } sp<NativeDisplayEventReceiver> receiver = - new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource, - eventRegistration, layerHandle); + new NativeDisplayEventReceiver(env, receiverWeak, vsyncEventDataWeak, messageQueue, + vsyncSource, eventRegistration, layerHandle); status_t status = receiver->initialize(); if (status) { String8 message; @@ -274,7 +315,9 @@ static jobject nativeGetLatestVsyncEventData(JNIEnv* env, jclass clazz, jlong re static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J", + {"nativeInit", + "(Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;Landroid/os/" + "MessageQueue;IIJ)J", (void*)nativeInit}, {"nativeGetDisplayEventReceiverFinalizer", "()J", (void*)nativeGetDisplayEventReceiverFinalizer}, @@ -291,8 +334,7 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gDisplayEventReceiverClassInfo.dispatchVsync = - GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", - "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V"); + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); gDisplayEventReceiverClassInfo.dispatchModeChanged = @@ -318,6 +360,15 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, "<init>", "(JJJ)V"); + gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, + "vsyncId", "J"); + gDisplayEventReceiverClassInfo.frameTimelineClassInfo.expectedPresentationTime = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, + "expectedPresentationTime", "J"); + gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, + "deadline", "J"); jclass vsyncEventDataClazz = FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData"); @@ -329,6 +380,17 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { "([Landroid/view/" "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V"); + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.preferredFrameTimelineIndex = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz, + "preferredFrameTimelineIndex", "I"); + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz, + "frameInterval", "J"); + gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameTimelines = + GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz, + "frameTimelines", + "[Landroid/view/DisplayEventReceiver$VsyncEventData$FrameTimeline;"); + return res; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b73623031dc1..5a0ac0f30cb2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4482,8 +4482,8 @@ <!-- Allows an application to be able to store and retrieve credentials from a remote device. @hide @SystemApi --> - <permission android:name="android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE" - android:protectionLevel="signature|privileged" /> + <permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS" + android:protectionLevel="signature|privileged|role" /> <!-- ========================================= --> <!-- Permissions for special development tools --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 47d771fea12d..07f353025f87 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4746,8 +4746,7 @@ <string name="accessibility_shortcut_disabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned off.</string> <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] --> - <string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use - <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string> + <string name="accessibility_shortcut_spoken_feedback">Release the volume keys. To turn on <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds.</string> <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]--> <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the accessibility button:</string> diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java index 16ed3ef42da3..17b064c12598 100644 --- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java +++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java @@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; +import android.hardware.Sensor; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -67,6 +68,24 @@ public class VirtualSensorConfigTest { } @Test + public void virtualSensorConfig_invalidName_throwsException() { + assertThrows( + NullPointerException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, null)); + } + + @Test + public void virtualSensorConfig_invalidType_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(Sensor.TYPE_ALL, SENSOR_NAME)); + + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(0, SENSOR_NAME)); + } + + @Test public void hardwareBufferDirectChannelTypeSupported_throwsException() { assertThrows( IllegalArgumentException.class, diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java index b6fc137471a4..ccf8085f87ff 100644 --- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java +++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java @@ -91,7 +91,7 @@ public class NameValueCacheTest { mConfigsCacheGenerationStore = new MemoryIntArray(2); mConfigsCacheGenerationStore.set(0, 123); mConfigsCacheGenerationStore.set(1, 456); - mSettingsCacheGenerationStore = new MemoryIntArray(2); + mSettingsCacheGenerationStore = new MemoryIntArray(3); mSettingsCacheGenerationStore.set(0, 234); mSettingsCacheGenerationStore.set(1, 567); mConfigsStorage = new HashMap<>(); @@ -163,6 +163,10 @@ public class NameValueCacheTest { Bundle incomingBundle = invocationOnMock.getArgument(4); String key = invocationOnMock.getArgument(3); String value = incomingBundle.getString(Settings.NameValueTable.VALUE); + boolean newSetting = false; + if (!mSettingsStorage.containsKey(key)) { + newSetting = true; + } mSettingsStorage.put(key, value); int currentGeneration; // Different settings have different generation codes @@ -173,12 +177,18 @@ public class NameValueCacheTest { currentGeneration = mSettingsCacheGenerationStore.get(1); mSettingsCacheGenerationStore.set(1, ++currentGeneration); } + if (newSetting) { + // Tracking the generation of all unset settings. + // Increment when a new setting is inserted + currentGeneration = mSettingsCacheGenerationStore.get(2); + mSettingsCacheGenerationStore.set(2, ++currentGeneration); + } return null; }); // Returns the value corresponding to a setting, or null if the setting - // doesn't have a value stored for it. Returns the generation key if the value isn't null - // and the caller asked for the generation key. + // doesn't have a value stored for it. Returns the generation key + // if the caller asked for the generation key. when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()), eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer( invocationOnMock -> { @@ -189,9 +199,15 @@ public class NameValueCacheTest { Bundle bundle = new Bundle(); bundle.putSerializable(Settings.NameValueTable.VALUE, value); - if (value != null && incomingBundle.containsKey( + if (incomingBundle.containsKey( Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { - int index = key.equals(SETTING) ? 0 : 1; + int index; + if (value != null) { + index = key.equals(SETTING) ? 0 : 1; + } else { + // special index for unset settings + index = 2; + } bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, mSettingsCacheGenerationStore); bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); @@ -361,16 +377,38 @@ public class NameValueCacheTest { } @Test - public void testCaching_nullSetting() throws Exception { + public void testCaching_unsetSetting() throws Exception { String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); verify(mMockIContentProvider, times(1)).call(any(), any(), eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); assertThat(returnedValue).isNull(); String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); - // Empty list won't be cached + // The first unset setting's generation number is cached + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue).isNull(); + + String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); verify(mMockIContentProvider, times(2)).call(any(), any(), eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); - assertThat(cachedValue).isNull(); + assertThat(returnedValue2).isNull(); + + String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING); + // The second unset setting's generation number is cached + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue2).isNull(); + + Settings.Secure.putString(mMockContentResolver, SETTING, "a"); + // The generation for unset settings should have changed + returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); + verify(mMockIContentProvider, times(3)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue2).isNull(); + + // The generation tracker for the first setting should have change because it's set now + returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(4)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isEqualTo("a"); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 36c0cb6dfe19..852fae695046 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -61,6 +61,7 @@ import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.NotificationListenerService; @@ -129,6 +130,15 @@ public class BubbleController implements ConfigurationChangeListener { private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; + // TODO(b/256873975) Should use proper flag when available to shell/launcher + /** + * Whether bubbles are showing in the bubble bar from launcher. This is only available + * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used + * to check all conditions that indicate if the bubble bar is in use. + */ + private static final boolean BUBBLE_BAR_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false); + private final Context mContext; private final BubblesImpl mImpl = new BubblesImpl(); private Bubbles.BubbleExpandListener mExpandListener; @@ -155,9 +165,6 @@ public class BubbleController implements ConfigurationChangeListener { private final ShellExecutor mBackgroundExecutor; - // Whether or not we should show bubbles pinned at the bottom of the screen. - private boolean mIsBubbleBarEnabled; - private BubbleLogger mLogger; private BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; @@ -540,10 +547,10 @@ public class BubbleController implements ConfigurationChangeListener { mDataRepository.removeBubblesForUser(removedUserId, parentUserId); } - // TODO(b/256873975): Should pass this into the constructor once flags are available to shell. - /** Sets whether the bubble bar is enabled (i.e. bubbles pinned to bottom on large screens). */ - public void setBubbleBarEnabled(boolean enabled) { - mIsBubbleBarEnabled = enabled; + /** Whether bubbles are showing in the bubble bar. */ + public boolean isShowingAsBubbleBar() { + // TODO(b/269670598): should also check that we're in gesture nav + return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen(); } /** Whether this userId belongs to the current user. */ @@ -612,12 +619,6 @@ public class BubbleController implements ConfigurationChangeListener { mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } - if (mIsBubbleBarEnabled && mBubblePositioner.isLargeScreen()) { - mBubblePositioner.setUsePinnedLocation(true); - } else { - mBubblePositioner.setUsePinnedLocation(false); - } - addToWindowManagerMaybe(); } @@ -1918,13 +1919,6 @@ public class BubbleController implements ConfigurationChangeListener { } @Override - public void setBubbleBarEnabled(boolean enabled) { - mMainExecutor.execute(() -> { - BubbleController.this.setBubbleBarEnabled(enabled); - }); - } - - @Override public void onNotificationPanelExpandedChanged(boolean expanded) { mMainExecutor.execute( () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6230d22ebe12..3fd09675a245 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -283,7 +283,7 @@ public class BubbleData { } boolean isShowingOverflow() { - return mShowingOverflow && (isExpanded() || mPositioner.showingInTaskbar()); + return mShowingOverflow && isExpanded(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 07c58527a815..5ea2450114f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -18,9 +18,6 @@ package com.android.wm.shell.bubbles; import static android.view.View.LAYOUT_DIRECTION_RTL; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.annotation.IntDef; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -39,8 +36,6 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; -import java.lang.annotation.Retention; - /** * Keeps track of display size, configuration, and specific bubble sizes. One place for all * placement and positioning calculations to refer to. @@ -50,15 +45,6 @@ public class BubblePositioner { ? "BubblePositioner" : BubbleDebugConfig.TAG_BUBBLES; - @Retention(SOURCE) - @IntDef({TASKBAR_POSITION_NONE, TASKBAR_POSITION_RIGHT, TASKBAR_POSITION_LEFT, - TASKBAR_POSITION_BOTTOM}) - @interface TaskbarPosition {} - public static final int TASKBAR_POSITION_NONE = -1; - public static final int TASKBAR_POSITION_RIGHT = 0; - public static final int TASKBAR_POSITION_LEFT = 1; - public static final int TASKBAR_POSITION_BOTTOM = 2; - /** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/ public static final int NUM_VISIBLE_WHEN_RESTING = 2; /** Indicates a bubble's height should be the maximum available space. **/ @@ -108,15 +94,9 @@ public class BubblePositioner { private int mOverflowHeight; private int mMinimumFlyoutWidthLargeScreen; - private PointF mPinLocation; private PointF mRestingStackPosition; private int[] mPaddings = new int[4]; - private boolean mShowingInTaskbar; - private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE; - private int mTaskbarIconSize; - private int mTaskbarSize; - public BubblePositioner(Context context, WindowManager windowManager) { mContext = context; mWindowManager = windowManager; @@ -153,27 +133,11 @@ public class BubblePositioner { + " insets: " + insets + " isLargeScreen: " + mIsLargeScreen + " isSmallTablet: " + mIsSmallTablet - + " bounds: " + bounds - + " showingInTaskbar: " + mShowingInTaskbar); + + " bounds: " + bounds); } updateInternal(mRotation, insets, bounds); } - /** - * Updates position information to account for taskbar state. - * - * @param taskbarPosition which position the taskbar is displayed in. - * @param showingInTaskbar whether the taskbar is being shown. - */ - public void updateForTaskbar(int iconSize, - @TaskbarPosition int taskbarPosition, boolean showingInTaskbar, int taskbarSize) { - mShowingInTaskbar = showingInTaskbar; - mTaskbarIconSize = iconSize; - mTaskbarPosition = taskbarPosition; - mTaskbarSize = taskbarSize; - update(); - } - @VisibleForTesting public void updateInternal(int rotation, Insets insets, Rect bounds) { mRotation = rotation; @@ -232,10 +196,6 @@ public class BubblePositioner { R.dimen.bubbles_flyout_min_width_large_screen); mMaxBubbles = calculateMaxBubbles(); - - if (mShowingInTaskbar) { - adjustForTaskbar(); - } } /** @@ -260,30 +220,6 @@ public class BubblePositioner { return mDefaultMaxBubbles; } - /** - * Taskbar insets appear as navigationBar insets, however, unlike navigationBar this should - * not inset bubbles UI as bubbles floats above the taskbar. This adjust the available space - * and insets to account for the taskbar. - */ - // TODO(b/171559950): When the insets are reported correctly we can remove this logic - private void adjustForTaskbar() { - // When bar is showing on edges... subtract that inset because we appear on top - if (mShowingInTaskbar && mTaskbarPosition != TASKBAR_POSITION_BOTTOM) { - WindowInsets metricInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); - Insets navBarInsets = metricInsets.getInsetsIgnoringVisibility( - WindowInsets.Type.navigationBars()); - int newInsetLeft = mInsets.left; - int newInsetRight = mInsets.right; - if (mTaskbarPosition == TASKBAR_POSITION_LEFT) { - mPositionRect.left -= navBarInsets.left; - newInsetLeft -= navBarInsets.left; - } else if (mTaskbarPosition == TASKBAR_POSITION_RIGHT) { - mPositionRect.right += navBarInsets.right; - newInsetRight -= navBarInsets.right; - } - mInsets = Insets.of(newInsetLeft, mInsets.top, newInsetRight, mInsets.bottom); - } - } /** * @return a rect of available screen space accounting for orientation, system bars and cutouts. @@ -327,14 +263,12 @@ public class BubblePositioner { * to the left or right side. */ public boolean showBubblesVertically() { - return isLandscape() || mShowingInTaskbar || mIsLargeScreen; + return isLandscape() || mIsLargeScreen; } /** Size of the bubble. */ public int getBubbleSize() { - return (mShowingInTaskbar && mTaskbarIconSize > 0) - ? mTaskbarIconSize - : mBubbleSize; + return mBubbleSize; } /** The amount of padding at the top of the screen that the bubbles avoid when being placed. */ @@ -699,9 +633,6 @@ public class BubblePositioner { /** The position the bubble stack should rest at when collapsed. */ public PointF getRestingPosition() { - if (mPinLocation != null) { - return mPinLocation; - } if (mRestingStackPosition == null) { return getDefaultStartPosition(); } @@ -713,9 +644,6 @@ public class BubblePositioner { * is being shown. */ public PointF getDefaultStartPosition() { - if (mPinLocation != null) { - return mPinLocation; - } // Start on the left if we're in LTR, right otherwise. final boolean startOnLeft = mContext.getResources().getConfiguration().getLayoutDirection() @@ -730,7 +658,6 @@ public class BubblePositioner { 1 /* default starts with 1 bubble */)); } - /** * Returns the region that the stack position must stay within. This goes slightly off the left * and right sides of the screen, below the status bar/cutout and above the navigation bar. @@ -751,39 +678,6 @@ public class BubblePositioner { } /** - * @return whether the bubble stack is pinned to the taskbar. - */ - public boolean showingInTaskbar() { - return mShowingInTaskbar; - } - - /** - * @return the taskbar position if set. - */ - public int getTaskbarPosition() { - return mTaskbarPosition; - } - - public int getTaskbarSize() { - return mTaskbarSize; - } - - /** - * In some situations bubbles will be pinned to a specific onscreen location. This sets whether - * bubbles should be pinned or not. - */ - public void setUsePinnedLocation(boolean usePinnedLocation) { - if (usePinnedLocation) { - mShowingInTaskbar = true; - mPinLocation = new PointF(mPositionRect.right - mBubbleSize, - mPositionRect.bottom - mBubbleSize); - } else { - mPinLocation = null; - mShowingInTaskbar = false; - } - } - - /** * Navigation bar has an area where system gestures can be started from. * * @return {@link Rect} for system navigation bar gesture zone diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index abe42eec7061..7d71089ef4fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -680,8 +680,6 @@ public class BubbleStackView extends FrameLayout // Re-show the expanded view if we hid it. showExpandedViewIfNeeded(); - } else if (mPositioner.showingInTaskbar()) { - mStackAnimationController.snapStackBack(); } else { // Fling the stack to the edge, and save whether or not it's going to end up on // the left side of the screen. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 1753cda895fe..4c0a93fb9355 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -273,11 +273,6 @@ public interface Bubbles { */ void onUserRemoved(int removedUserId); - /** - * Sets whether bubble bar should be enabled or not. - */ - void setBubbleBarEnabled(boolean enabled); - /** Listener to find out about stack expansion / collapse events. */ interface BubbleExpandListener { /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java index 0ee0ea60a1bc..5533842f2d89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java @@ -417,23 +417,9 @@ public class StackAnimationController extends } /** - * Snaps the stack back to the previous resting position. - */ - public void snapStackBack() { - if (mLayout == null) { - return; - } - PointF p = getStackPositionAlongNearestHorizontalEdge(); - springStackAfterFling(p.x, p.y); - } - - /** * Where the stack would be if it were snapped to the nearest horizontal edge (left or right). */ public PointF getStackPositionAlongNearestHorizontalEdge() { - if (mPositioner.showingInTaskbar()) { - return mPositioner.getRestingPosition(); - } final PointF stackPos = getStackPosition(); final boolean onLeft = mLayout.isFirstChildXLeftOfCenter(stackPos.x); final RectF bounds = mPositioner.getAllowableStackPositionRegion(getBubbleCount()); diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 7e9443b467ef..c39a6dbd17da 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -32,6 +32,7 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.NullPointerException; import java.util.concurrent.Executor; /** @@ -271,7 +272,12 @@ public class Filter implements AutoCloseable { mExecutor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { - mCallback.onFilterStatusChanged(this, status); + try { + mCallback.onFilterStatusChanged(this, status); + } + catch (NullPointerException e) { + Log.d(TAG, "catch exception:" + e); + } } } }); @@ -285,7 +291,12 @@ public class Filter implements AutoCloseable { mExecutor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { - mCallback.onFilterEvent(this, events); + try { + mCallback.onFilterEvent(this, events); + } + catch (NullPointerException e) { + Log.d(TAG, "catch exception:" + e); + } } else { for (FilterEvent event : events) { if (event instanceof MediaEvent) { diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt index 1a3c0ab67b93..47bf85d8417b 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt @@ -19,6 +19,7 @@ package com.android.settingslib.spaprivileged.template.app import android.content.Context import android.content.pm.ApplicationInfo import android.text.format.Formatter +import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.produceState @@ -30,18 +31,26 @@ import com.android.settingslib.spaprivileged.model.app.userHandle import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +private const val TAG = "AppStorageSize" + @Composable fun ApplicationInfo.getStorageSize(): State<String> { val context = LocalContext.current return produceState(initialValue = stringResource(R.string.summary_placeholder)) { withContext(Dispatchers.IO) { - value = Formatter.formatFileSize(context, calculateSizeBytes(context)) + val sizeBytes = calculateSizeBytes(context) + value = if (sizeBytes != null) Formatter.formatFileSize(context, sizeBytes) else "" } } } -private fun ApplicationInfo.calculateSizeBytes(context: Context): Long { +private fun ApplicationInfo.calculateSizeBytes(context: Context): Long? { val storageStatsManager = context.storageStatsManager - val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle) - return stats.codeBytes + stats.dataBytes + return try { + val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle) + stats.codeBytes + stats.dataBytes + } catch (e: Exception) { + Log.w(TAG, "Failed to query stats: $e") + null + } } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt index fcacc34ba881..e3af58702445 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt @@ -20,6 +20,7 @@ import android.app.usage.StorageStats import android.app.usage.StorageStatsManager import android.content.Context import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager.NameNotFoundException import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.test.junit4.createComposeRule @@ -60,9 +61,11 @@ class AppStorageSizeTest { @Before fun setUp() { whenever(context.storageStatsManager).thenReturn(storageStatsManager) - whenever(storageStatsManager.queryStatsForPackage( - app.storageUuid, app.packageName, app.userHandle - )).thenReturn(STATS) + whenever( + storageStatsManager.queryStatsForPackage( + app.storageUuid, app.packageName, app.userHandle + ) + ).thenReturn(STATS) } @Test @@ -78,6 +81,24 @@ class AppStorageSizeTest { composeTestRule.waitUntil { storageSize.value == "120 B" } } + @Test + fun getStorageSize_throwException() { + var storageSize = stateOf("Computing") + whenever( + storageStatsManager.queryStatsForPackage( + app.storageUuid, app.packageName, app.userHandle + ) + ).thenThrow(NameNotFoundException()) + + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { + storageSize = app.getStorageSize() + } + } + + composeTestRule.waitUntil { storageSize.value == "" } + } + companion object { private val STATS = StorageStats().apply { codeBytes = 100 diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java index 7f3b0ff8c838..db6cc1a39ef1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java @@ -16,6 +16,7 @@ package com.android.providers.settings; +import android.annotation.NonNull; import android.os.Bundle; import android.provider.Settings; import android.util.ArrayMap; @@ -59,6 +60,10 @@ final class GenerationRegistry { // Maximum size of an individual backing store static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize(); + // Use an empty string to track the generation number of all non-predefined, unset settings + // The generation number is only increased when a new non-predefined setting is inserted + private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = ""; + public GenerationRegistry(Object lock) { mLock = lock; } @@ -72,6 +77,10 @@ final class GenerationRegistry { (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); // Only store the prefix if the mutated setting is a config final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name; + incrementGenerationInternal(key, indexMapKey); + } + + private void incrementGenerationInternal(int key, @NonNull String indexMapKey) { synchronized (mLock) { final MemoryIntArray backingStore = getBackingStoreLocked(key, /* createIfNotExist= */ false); @@ -87,7 +96,8 @@ final class GenerationRegistry { final int generation = backingStore.get(index) + 1; backingStore.set(index, generation); if (DEBUG) { - Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey + Slog.i(LOG_TAG, "Incremented generation for " + + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey) + " key:" + SettingsState.keyToString(key) + " at index:" + index); } @@ -98,6 +108,18 @@ final class GenerationRegistry { } } + // A new, non-predefined setting has been inserted, increment the tracking number for all unset + // settings + public void incrementGenerationForUnsetSettings(int key) { + final boolean isConfig = + (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); + if (isConfig) { + // No need to track new settings for configs + return; + } + incrementGenerationInternal(key, DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS); + } + /** * Return the backing store's reference, the index and the current generation number * of a cached setting. If it was not in the backing store, first create the entry in it before @@ -124,8 +146,8 @@ final class GenerationRegistry { bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, backingStore.get(index)); if (DEBUG) { - Slog.i(LOG_TAG, "Exported index:" + index - + " for setting:" + indexMapKey + Slog.i(LOG_TAG, "Exported index:" + index + " for " + + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey) + " key:" + SettingsState.keyToString(key)); } } catch (IOException e) { @@ -135,6 +157,10 @@ final class GenerationRegistry { } } + public void addGenerationDataForUnsetSettings(Bundle bundle, int key) { + addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS); + } + public void onUserRemoved(int userId) { final int secureKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SECURE, userId); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index ba275ebca168..418011acf6f3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2327,11 +2327,15 @@ public class SettingsProvider extends ContentProvider { result.putString(Settings.NameValueTable.VALUE, (setting != null && !setting.isNull()) ? setting.getValue() : null); - if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) { - // Don't track generation for non-existent settings unless the name is predefined - synchronized (mLock) { + synchronized (mLock) { + if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) { + // Individual generation tracking for predefined settings even if they are unset mSettingsRegistry.mGenerationRegistry.addGenerationData(result, SettingsState.makeKey(type, userId), name); + } else { + // All non-predefined, unset settings are tracked using the same generation number + mSettingsRegistry.mGenerationRegistry.addGenerationDataForUnsetSettings(result, + SettingsState.makeKey(type, userId)); } } return result; @@ -2345,7 +2349,8 @@ public class SettingsProvider extends ContentProvider { } else if (type == SETTINGS_TYPE_SYSTEM) { return sAllSystemSettings.contains(name); } else { - return false; + // Consider all config settings predefined because they are used by system apps only + return type == SETTINGS_TYPE_CONFIG; } } @@ -2354,14 +2359,13 @@ public class SettingsProvider extends ContentProvider { Bundle result = new Bundle(); result.putSerializable(Settings.NameValueTable.VALUE, keyValues); if (trackingGeneration) { - // Track generation even if the namespace is empty because this is for system apps synchronized (mLock) { + // Track generation even if namespace is empty because this is for system apps only mSettingsRegistry.mGenerationRegistry.addGenerationData(result, - mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG, - UserHandle.USER_SYSTEM).mKey, prefix); + SettingsState.makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM), + prefix); } } - return result; } @@ -3052,10 +3056,15 @@ public class SettingsProvider extends ContentProvider { final int key = makeKey(type, userId); boolean success = false; + boolean isNewSetting = false; SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState != null) { + if (!settingsState.hasSetting(name)) { + isNewSetting = true; + } success = settingsState.insertSettingLocked(name, value, - tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore); + tag, makeDefault, forceNonSystemPackage, packageName, + overrideableByRestore); } if (success && criticalSettings != null && criticalSettings.contains(name)) { @@ -3064,6 +3073,11 @@ public class SettingsProvider extends ContentProvider { if (forceNotify || success) { notifyForSettingsChange(key, name); + if (isNewSetting && !isSettingPreDefined(name, type)) { + // Increment the generation number for all null settings because a new + // non-predefined setting has been inserted + mGenerationRegistry.incrementGenerationForUnsetSettings(key); + } } if (success) { logSettingChanged(userId, name, type, CHANGE_TYPE_INSERT); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index c3888268a61d..4d8705f135af 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -759,6 +759,12 @@ final class SettingsState { mPackageToMemoryUsage.put(packageName, newSize); } + public boolean hasSetting(String name) { + synchronized (mLock) { + return hasSettingLocked(name); + } + } + @GuardedBy("mLock") private boolean hasSettingLocked(String name) { return mSettings.indexOfKey(name) >= 0; diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java index d34fe6943153..6ec8146baee0 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java @@ -151,6 +151,26 @@ public class GenerationRegistryTest { checkBundle(b, 0, 1, false); } + @Test + public void testUnsetSettings() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + checkBundle(b, 0, 1, false); + generationRegistry.addGenerationDataForUnsetSettings(b, secureKey); + checkBundle(b, 1, 1, false); + generationRegistry.addGenerationDataForUnsetSettings(b, secureKey); + // Test that unset settings always have the same index + checkBundle(b, 1, 1, false); + generationRegistry.incrementGenerationForUnsetSettings(secureKey); + // Test that the generation number of the unset settings have increased + generationRegistry.addGenerationDataForUnsetSettings(b, secureKey); + checkBundle(b, 1, 2, false); + } + + private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull) throws IOException { final MemoryIntArray array = getArray(b); diff --git a/packages/SystemUI/animation/.gitignore b/packages/SystemUI/animation/.gitignore new file mode 100644 index 000000000000..f9a33dbbcc7e --- /dev/null +++ b/packages/SystemUI/animation/.gitignore @@ -0,0 +1,9 @@ +.idea/ +.gradle/ +gradle/ +build/ +gradlew* +local.properties +*.iml +android.properties +buildSrc
\ No newline at end of file diff --git a/packages/SystemUI/animation/build.gradle b/packages/SystemUI/animation/build.gradle new file mode 100644 index 000000000000..939455fa44ac --- /dev/null +++ b/packages/SystemUI/animation/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +// TODO: Pull out surfaceeffects outside of src and have separate build files there. +android { + sourceSets { + main { + java.srcDirs = ["${SYS_UI_DIR}/animation/src/com/android/systemui/surfaceeffects/"] + manifest.srcFile "${SYS_UI_DIR}/animation/AndroidManifest.xml" + } + } + + compileSdk 33 + + defaultConfig { + minSdk 33 + targetSdk 33 + } + + lintOptions { + abortOnError false + } + tasks.lint.enabled = false + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } + kotlinOptions { + jvmTarget = '1.8' + freeCompilerArgs = ["-Xjvm-default=all"] + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0" + implementation 'androidx.core:core-animation:1.0.0-alpha02' + implementation 'androidx.core:core-ktx:1.9.0' +} diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml index 58fe368ce4e1..97bd18e0442b 100644 --- a/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml +++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml @@ -27,7 +27,7 @@ <shape android:shape="rectangle"> <corners android:radius="@dimen/magnifier_outer_corner_radius" /> <stroke - android:color="@android:color/black" + android:color="@color/magnification_drag_handle_stroke" android:width="@dimen/magnifier_stroke_width"/> </shape> </item> diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml index a52e8053d8a0..66617e16b33a 100644 --- a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml +++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml @@ -16,7 +16,7 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <stroke - android:color="@android:color/black" + android:color="@color/magnification_drag_handle_stroke" android:width="@dimen/magnifier_stroke_width"/> <corners android:radius="@dimen/magnifier_corner_radius" /> <solid android:color="@color/magnification_border_color" /> diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml new file mode 100644 index 000000000000..e367f50632a9 --- /dev/null +++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <stroke + android:color="@color/magnification_border_color" + android:width="@dimen/magnifier_stroke_width"/> + <corners android:radius="@dimen/magnifier_corner_radius" /> + <solid android:color="@color/magnification_drag_handle_background_change" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml index 641bb4381fc8..079600734bc8 100644 --- a/packages/SystemUI/res/drawable/ic_move_magnification.xml +++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml @@ -20,6 +20,6 @@ android:viewportHeight="24" android:tint="?attr/colorControlNormal"> <path - android:fillColor="@color/magnification_drag_handle_tint" + android:fillColor="@color/magnification_drag_handle_stroke" android:pathData="M12,15Q10.75,15 9.875,14.125Q9,13.25 9,12Q9,10.75 9.875,9.875Q10.75,9 12,9Q13.25,9 14.125,9.875Q15,10.75 15,12Q15,13.25 14.125,14.125Q13.25,15 12,15ZM12,22 L7.75,17.75 9.15,16.35 12,19.15 14.85,16.35 16.25,17.75ZM6.25,16.25 L2,12 6.25,7.75 7.65,9.15 4.85,12 7.65,14.85ZM9.15,7.65 L7.75,6.25 12,2 16.25,6.25 14.85,7.65 12,4.85ZM17.75,16.25 L16.35,14.85 19.15,12 16.35,9.15 17.75,7.75 22,12Z"/> </vector> diff --git a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml new file mode 100644 index 000000000000..de0a6201cb09 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <!-- gradient from 25% in the center to 100% at edges --> + <gradient + android:type="radial" + android:gradientRadius="40%p" + android:startColor="#AE000000" + android:endColor="#00000000" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml index c54c4e48d13d..a4aeba1dbcd6 100644 --- a/packages/SystemUI/res/layout/media_recommendation_view.xml +++ b/packages/SystemUI/res/layout/media_recommendation_view.xml @@ -22,9 +22,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:translationZ="0dp" - android:scaleType="centerCrop" + android:scaleType="matrix" android:adjustViewBounds="true" android:clipToOutline="true" + android:layerType="hardware" android:background="@drawable/bg_smartspace_media_item"/> <!-- App icon --> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index c6cc0bc01116..d4ebd100a91d 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -163,8 +163,8 @@ <color name="magnification_border_color">#F29900</color> <color name="magnification_switch_button_color">#7F000000</color> <color name="magnification_drag_corner_background">#E5FFFFFF</color> - <color name="magnification_drag_handle_color">#B3000000</color> - <color name="magnification_drag_handle_tint">#111111</color> + <color name="magnification_drag_handle_stroke">#000000</color> + <color name="magnification_drag_handle_background_change">#111111</color> <color name="accessibility_magnifier_bg">#FCFCFC</color> <color name="accessibility_magnifier_bg_stroke">#E0E0E0</color> <color name="accessibility_magnifier_icon_color">#252525</color> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 6dd359cb6351..45a5ce34f830 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -18,7 +18,6 @@ package com.android.systemui.shared.system; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityTaskManager.getService; import android.annotation.NonNull; @@ -45,6 +44,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; import android.util.Log; +import android.view.Display; import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationTarget; @@ -112,6 +112,13 @@ public class ActivityManagerWrapper { } /** + * @see #getRunningTasks(boolean , int) + */ + public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { + return getRunningTasks(filterOnlyVisibleRecents, Display.INVALID_DISPLAY); + } + + /** * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen, * we'll get back 2 activities for each split app and one for launcher. Launcher might be more * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss @@ -120,10 +127,12 @@ public class ActivityManagerWrapper { * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks * filtering only for tasks that can be visible in the recent tasks list. */ - public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { + public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents, + int displayId) { // Note: The set of running tasks from the system is ordered by recency List<ActivityManager.RunningTaskInfo> tasks = - mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents); + mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, + filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId); return tasks.toArray(new RunningTaskInfo[tasks.size()]); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 21d35c9579ea..30e2a0b613ee 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -877,7 +877,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) { mBackgroundExecutor.execute( - () -> mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId)); + () -> { + mLogger.logReportSuccessfulBiometricUnlock(isStrongBiometric, userId); + mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId); + }); } private void handleFingerprintAuthFailed() { @@ -2530,11 +2533,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // If this message exists, we should not authenticate again until this message is // consumed by the handler if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { + mLogger.logHandlerHasAuthContinueMsgs(action); return; } // don't start running fingerprint until they're registered if (!mAuthController.areAllFingerprintAuthenticatorsRegistered()) { + mLogger.d("All FP authenticators not registered, skipping FP listening state update"); return; } final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported()); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt index bc0bd8c53d26..20f90072161b 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt @@ -16,6 +16,7 @@ package com.android.keyguard.logging +import android.hardware.biometrics.BiometricSourceType import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.dagger.BiometricLog import com.android.systemui.plugins.log.LogBuffer @@ -157,6 +158,36 @@ class BiometricUnlockLogger @Inject constructor(@BiometricLog private val logBuf } ) } + + fun deferringAuthenticationDueToSleep( + userId: Int, + biometricSourceType: BiometricSourceType, + alreadyPendingAuth: Boolean + ) { + logBuffer.log( + TAG, + DEBUG, + { + int1 = userId + str1 = biometricSourceType.name + bool2 = alreadyPendingAuth + }, + { + "onBiometricAuthenticated, deferring auth: userId: $int1, " + + "biometricSourceType: $str1, " + + "goingToSleep: true, " + + "mPendingAuthentication != null: $bool2" + } + ) + } + + fun finishedGoingToSleepWithPendingAuth() { + logBuffer.log( + TAG, + LogLevel.DEBUG, + "onFinishedGoingToSleep with pendingAuthenticated != null" + ) + } } private fun wakeAndUnlockModeToString(mode: Int): String { diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt index 379c78ad8d0e..51aca070b180 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt @@ -16,6 +16,7 @@ package com.android.keyguard.logging +import com.android.systemui.biometrics.AuthRippleController import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController import com.android.systemui.log.dagger.KeyguardLog import com.android.systemui.plugins.log.LogBuffer @@ -120,4 +121,29 @@ constructor( "type=${KeyguardIndicationRotateTextViewController.indicationTypeToString(type)}" } } + + fun notShowingUnlockRipple(keyguardNotShowing: Boolean, unlockNotAllowed: Boolean) { + buffer.log( + AuthRippleController.TAG, + LogLevel.DEBUG, + { + bool1 = keyguardNotShowing + bool2 = unlockNotAllowed + }, + { "Not showing unlock ripple: keyguardNotShowing: $bool1, unlockNotAllowed: $bool2" } + ) + } + + fun showingUnlockRippleAt(x: Int, y: Int, context: String) { + buffer.log( + AuthRippleController.TAG, + LogLevel.DEBUG, + { + int1 = x + int2 = y + str1 = context + }, + { "Showing unlock ripple with center (x, y): ($int1, $int2), context: $str1" } + ) + } } diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index fb2c02ad8c48..2403d1116360 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -584,6 +584,30 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } + fun logReportSuccessfulBiometricUnlock(isStrongBiometric: Boolean, userId: Int) { + logBuffer.log( + TAG, + DEBUG, + { + bool1 = isStrongBiometric + int1 = userId + }, + { "reporting successful biometric unlock: isStrongBiometric: $bool1, userId: $int1" } + ) + } + + fun logHandlerHasAuthContinueMsgs(action: Int) { + logBuffer.log( + TAG, + DEBUG, + { int1 = action }, + { + "MSG_BIOMETRIC_AUTHENTICATION_CONTINUE already queued up, " + + "ignoring updating FP listening state to $int1" + } + ) + } + fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) { logBuffer.log( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index d35c77c3b231..d811d30b5693 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -37,6 +37,8 @@ import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -1398,6 +1400,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWindowMagnifierCallback.onPerformScaleAction(mDisplayId, A11Y_ACTION_SCALE_RANGE.clamp(scale)); } + + @Override + public void onSettingsPanelVisibilityChanged(boolean shown) { + updateDragHandleResourcesIfNeeded(/* settingsPanelIsShown= */ shown); + } }; @Override @@ -1436,6 +1443,20 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } + private void updateDragHandleResourcesIfNeeded(boolean settingsPanelIsShown) { + mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown + ? R.drawable.accessibility_window_magnification_drag_handle_background_change + : R.drawable.accessibility_window_magnification_drag_handle_background)); + + PorterDuffColorFilter filter = new PorterDuffColorFilter( + mContext.getColor(settingsPanelIsShown + ? R.color.magnification_border_color + : R.color.magnification_drag_handle_stroke), + PorterDuff.Mode.SRC_ATOP); + + mDragView.setColorFilter(filter); + } + private void animateBounceEffect() { final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView, PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1), diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index e1f3c6cc5c3b..9ad64e293fe5 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -323,6 +323,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } mContext.unregisterReceiver(mScreenOffReceiver); + mCallback.onSettingsPanelVisibilityChanged(/* shown= */ false); } public void showSettingPanel() { @@ -361,6 +362,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest // Exclude magnification switch button from system gesture area. setSystemGestureExclusion(); mIsVisible = true; + mCallback.onSettingsPanelVisibilityChanged(/* shown= */ true); } mContext.registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java index 22ec65001101..1d833402b1f4 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java @@ -61,4 +61,11 @@ public interface WindowMagnificationSettingsCallback { * 1 : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, 2 : ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW */ void onModeSwitch(int newMode); + + /** + * Called when the visibility of the magnification settings panel changed. + * + * @param shown The visibility of the magnification settings panel. + */ + void onSettingsPanelVisibilityChanged(boolean shown); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index d561cd7af7f0..93b57dc127dd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricSourceType import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.keyguard.logging.KeyguardLogger import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.Interpolators @@ -74,6 +75,7 @@ class AuthRippleController @Inject constructor( private val udfpsControllerProvider: Provider<UdfpsController>, private val statusBarStateController: StatusBarStateController, private val featureFlags: FeatureFlags, + private val logger: KeyguardLogger, rippleView: AuthRippleView? ) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback, WakefulnessLifecycle.Observer { @@ -120,8 +122,11 @@ class AuthRippleController @Inject constructor( } fun showUnlockRipple(biometricSourceType: BiometricSourceType) { - if (!keyguardStateController.isShowing || - !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(biometricSourceType)) { + val keyguardNotShowing = !keyguardStateController.isShowing + val unlockNotAllowed = !keyguardUpdateMonitor + .isUnlockingWithBiometricAllowed(biometricSourceType) + if (keyguardNotShowing || unlockNotAllowed) { + logger.notShowingUnlockRipple(keyguardNotShowing, unlockNotAllowed) return } @@ -138,6 +143,7 @@ class AuthRippleController @Inject constructor( Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y) ) ) + logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius") showUnlockedRipple() } } else if (biometricSourceType == BiometricSourceType.FACE) { @@ -155,6 +161,7 @@ class AuthRippleController @Inject constructor( Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y) ) ) + logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple") showUnlockedRipple() } } @@ -391,5 +398,6 @@ class AuthRippleController @Inject constructor( companion object { const val RIPPLE_ANIMATION_DURATION: Long = 1533 + const val TAG = "AuthRippleController" } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index bc0f9950f865..f83885b7bb32 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -38,6 +38,7 @@ import javax.inject.Inject; public class FalsingDataProvider { private static final long MOTION_EVENT_AGE_MS = 1000; + private static final long DROP_EVENT_THRESHOLD_MS = 50; private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI); private final int mWidthPixels; @@ -60,6 +61,7 @@ public class FalsingDataProvider { private float mAngle = 0; private MotionEvent mFirstRecentMotionEvent; private MotionEvent mLastMotionEvent; + private boolean mDropLastEvent; private boolean mJustUnlockedWithFace; private boolean mA11YAction; @@ -95,6 +97,12 @@ public class FalsingDataProvider { // Ensure prior gesture was completed. May be a no-op. completePriorGesture(); } + + // Drop the gesture closing event if it is close in time to a previous ACTION_MOVE event. + // The reason is that the closing ACTION_UP event of a swipe can be a bit offseted from the + // previous ACTION_MOVE event and when it happens, it makes some classifiers fail. + mDropLastEvent = shouldDropEvent(motionEvent); + mRecentMotionEvents.addAll(motionEvents); FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size()); @@ -129,6 +137,7 @@ public class FalsingDataProvider { mPriorMotionEvents = mRecentMotionEvents; mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS); } + mDropLastEvent = false; mA11YAction = false; } @@ -150,8 +159,18 @@ public class FalsingDataProvider { return mYdpi; } + /** + * Get the {@link MotionEvent}s of the most recent gesture. + * + * Note that this list may not include the last recorded event. + * @see #mDropLastEvent + */ public List<MotionEvent> getRecentMotionEvents() { - return mRecentMotionEvents; + if (!mDropLastEvent || mRecentMotionEvents.isEmpty()) { + return mRecentMotionEvents; + } else { + return mRecentMotionEvents.subList(0, mRecentMotionEvents.size() - 1); + } } public List<MotionEvent> getPriorMotionEvents() { @@ -169,7 +188,12 @@ public class FalsingDataProvider { return mFirstRecentMotionEvent; } - /** Get the last recorded {@link MotionEvent}. */ + /** + * Get the last {@link MotionEvent} of the most recent gesture. + * + * Note that this may be the event prior to the last recorded event. + * @see #mDropLastEvent + */ public MotionEvent getLastMotionEvent() { recalculateData(); return mLastMotionEvent; @@ -236,12 +260,13 @@ public class FalsingDataProvider { return; } - if (mRecentMotionEvents.isEmpty()) { + List<MotionEvent> recentMotionEvents = getRecentMotionEvents(); + if (recentMotionEvents.isEmpty()) { mFirstRecentMotionEvent = null; mLastMotionEvent = null; } else { - mFirstRecentMotionEvent = mRecentMotionEvents.get(0); - mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1); + mFirstRecentMotionEvent = recentMotionEvents.get(0); + mLastMotionEvent = recentMotionEvents.get(recentMotionEvents.size() - 1); } calculateAngleInternal(); @@ -249,6 +274,17 @@ public class FalsingDataProvider { mDirty = false; } + private boolean shouldDropEvent(MotionEvent event) { + if (mRecentMotionEvents.size() < 3) return false; + + MotionEvent lastEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1); + boolean isCompletingGesture = event.getActionMasked() == MotionEvent.ACTION_UP + && lastEvent.getActionMasked() == MotionEvent.ACTION_MOVE; + boolean isRecentEvent = + event.getEventTime() - lastEvent.getEventTime() < DROP_EVENT_THRESHOLD_MS; + return isCompletingGesture && isRecentEvent; + } + private void calculateAngleInternal() { if (mRecentMotionEvents.size() < 2) { mAngle = Float.MAX_VALUE; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java index 4773f2a3b13e..51aede79b581 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java @@ -183,7 +183,7 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> { @Override public List<MotionEvent> subList(int fromIndex, int toIndex) { - throw new UnsupportedOperationException(); + return mMotionEvents.subList(fromIndex, toIndex); } class Iter implements ListIterator<MotionEvent> { diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index c214f5341450..2501be93d189 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -263,10 +263,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv @Override // ClipboardListener.ClipboardOverlay public void setClipData(ClipData data, String source) { ClipboardModel model = ClipboardModel.fromClipData(mContext, mClipboardUtils, data, source); - if (mExitAnimator != null && mExitAnimator.isRunning()) { + boolean wasExiting = (mExitAnimator != null && mExitAnimator.isRunning()); + if (wasExiting) { mExitAnimator.cancel(); } - boolean shouldAnimate = !model.dataMatches(mClipboardModel); + boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting; mClipboardModel = model; mClipboardLogger.setClipSource(mClipboardModel.getSource()); if (shouldAnimate) { @@ -313,15 +314,19 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mOnPreviewTapped = this::editText; break; case IMAGE: - if (model.isSensitive() || model.loadThumbnail(mContext) != null) { - mView.showImagePreview( - model.isSensitive() ? null : model.loadThumbnail(mContext)); - mView.setEditAccessibilityAction(true); - mOnPreviewTapped = () -> editImage(model.getUri()); - } else { - // image loading failed - mView.showDefaultTextPreview(); - } + mBgExecutor.execute(() -> { + if (model.isSensitive() || model.loadThumbnail(mContext) != null) { + mView.post(() -> { + mView.showImagePreview( + model.isSensitive() ? null : model.loadThumbnail(mContext)); + mView.setEditAccessibilityAction(true); + }); + mOnPreviewTapped = () -> editImage(model.getUri()); + } else { + // image loading failed + mView.post(mView::showDefaultTextPreview); + } + }); break; case URI: case OTHER: @@ -363,15 +368,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private void classifyText(ClipboardModel model) { mBgExecutor.execute(() -> { - Optional<RemoteAction> remoteAction = mClipboardUtils.getAction( - model.getText(), model.getTextLinks(), model.getSource()); + Optional<RemoteAction> remoteAction = + mClipboardUtils.getAction(model.getTextLinks(), model.getSource()); if (model.equals(mClipboardModel)) { remoteAction.ifPresent(action -> { mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN); - mView.setActionChip(action, () -> { + mView.post(() -> mView.setActionChip(action, () -> { mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); animateOut(); - }); + })); }); } }); diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java index a85f8b9357f5..25caaeac2c38 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java @@ -39,6 +39,9 @@ import javax.inject.Inject; class ClipboardOverlayUtils { + // minimum proportion of entire text an entity must take up, to be considered for smart actions + private static final float MINIMUM_ENTITY_PROPORTION = .8f; + private final TextClassifier mTextClassifier; @Inject @@ -65,19 +68,23 @@ class ClipboardOverlayUtils { return false; } - public Optional<RemoteAction> getAction(CharSequence text, TextLinks textLinks, String source) { - return getActions(text, textLinks).stream().filter(remoteAction -> { + public Optional<RemoteAction> getAction(TextLinks textLinks, String source) { + return getActions(textLinks).stream().filter(remoteAction -> { ComponentName component = remoteAction.getActionIntent().getIntent().getComponent(); return component != null && !TextUtils.equals(source, component.getPackageName()); }).findFirst(); } - private ArrayList<RemoteAction> getActions(CharSequence text, TextLinks textLinks) { + private ArrayList<RemoteAction> getActions(TextLinks textLinks) { ArrayList<RemoteAction> actions = new ArrayList<>(); for (TextLinks.TextLink link : textLinks.getLinks()) { - TextClassification classification = mTextClassifier.classifyText( - text, link.getStart(), link.getEnd(), null); - actions.addAll(classification.getActions()); + // skip classification for incidental entities + if (link.getEnd() - link.getStart() + >= textLinks.getText().length() * MINIMUM_ENTITY_PROPORTION) { + TextClassification classification = mTextClassifier.classifyText( + textLinks.getText(), link.getStart(), link.getEnd(), null); + actions.addAll(classification.getActions()); + } } return actions; } @@ -92,9 +99,13 @@ class ClipboardOverlayUtils { private ArrayList<RemoteAction> getActions(ClipData.Item item) { ArrayList<RemoteAction> actions = new ArrayList<>(); for (TextLinks.TextLink link : item.getTextLinks().getLinks()) { - TextClassification classification = mTextClassifier.classifyText( - item.getText(), link.getStart(), link.getEnd(), null); - actions.addAll(classification.getActions()); + // skip classification for incidental entities + if (link.getEnd() - link.getStart() + >= item.getText().length() * MINIMUM_ENTITY_PROPORTION) { + TextClassification classification = mTextClassifier.classifyText( + item.getText(), link.getStart(), link.getEnd(), null); + actions.addAll(classification.getActions()); + } } return actions; } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 58174153a837..95b37ac5545d 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -438,7 +438,9 @@ object Flags { ) // TODO(b/256873975): Tracking Bug - @JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar") + @JvmField + @Keep + val WM_BUBBLE_BAR = sysPropBooleanFlag(1111, "persist.wm.debug.bubble_bar", default = false) // TODO(b/260271148): Tracking bug @Keep diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt index b0f9c4edb073..d078688e5944 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt @@ -21,6 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyboard.backlight.ui.KeyboardBacklightDialogCoordinator import javax.inject.Inject /** A [CoreStartable] that launches components interested in physical keyboard interaction. */ @@ -28,11 +29,12 @@ import javax.inject.Inject class PhysicalKeyboardCoreStartable @Inject constructor( + private val keyboardBacklightDialogCoordinator: KeyboardBacklightDialogCoordinator, private val featureFlags: FeatureFlags, ) : CoreStartable { override fun start() { if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) { - // TODO(b/268645743) start listening for keyboard backlight brightness + keyboardBacklightDialogCoordinator.startListening() } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt new file mode 100644 index 000000000000..65e70b319923 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyboard.data.repository.KeyboardRepository +import com.android.systemui.keyboard.shared.model.BacklightModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf + +/** Allows listening to changes to keyboard backlight level */ +@SysUISingleton +class KeyboardBacklightInteractor +@Inject +constructor( + private val keyboardRepository: KeyboardRepository, +) { + + /** Emits current backlight level as [BacklightModel] or null if keyboard is not connected */ + val backlight: Flow<BacklightModel?> = + keyboardRepository.keyboardConnected.flatMapLatest { connected -> + if (connected) keyboardRepository.backlight else flowOf(null) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt new file mode 100644 index 000000000000..85d0379a77db --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui + +import android.content.Context +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog +import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight + * indicator + */ +@SysUISingleton +class KeyboardBacklightDialogCoordinator +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val context: Context, + private val viewModel: BacklightDialogViewModel, +) { + + var dialog: KeyboardBacklightDialog? = null + + fun startListening() { + applicationScope.launch { + viewModel.dialogContent.collect { dialogViewModel -> + if (dialogViewModel != null) { + if (dialog == null) { + dialog = KeyboardBacklightDialog(context, dialogViewModel) + // pass viewModel and show + } + } else { + dialog?.dismiss() + dialog = null + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt new file mode 100644 index 000000000000..b68a2a84b5d1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui.view + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel + +class KeyboardBacklightDialog(context: Context, val viewModel: BacklightDialogContentViewModel) : + Dialog(context) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // TODO(b/268650355) Implement the dialog + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt new file mode 100644 index 000000000000..3ef0ca39b8f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui.viewmodel + +data class BacklightDialogContentViewModel(val currentValue: Int, val maxValue: Int) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt new file mode 100644 index 000000000000..86abca5faaf3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui.viewmodel + +import android.view.accessibility.AccessibilityManager +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import javax.inject.Inject +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map + +/** + * Responsible for dialog visibility and content - emits [BacklightDialogContentViewModel] if dialog + * should be shown and hidden otherwise + */ +@SysUISingleton +class BacklightDialogViewModel +@Inject +constructor( + interactor: KeyboardBacklightInteractor, + private val accessibilityManagerWrapper: AccessibilityManagerWrapper, +) { + + private val timeoutMillis: Long + get() = + accessibilityManagerWrapper + .getRecommendedTimeoutMillis( + DEFAULT_DIALOG_TIMEOUT_MILLIS, + AccessibilityManager.FLAG_CONTENT_ICONS + ) + .toLong() + + val dialogContent: Flow<BacklightDialogContentViewModel?> = + interactor.backlight + .filterNotNull() + .map { BacklightDialogContentViewModel(it.level, it.maxLevel) } + .timeout(timeoutMillis, emitAfterTimeout = null) + + private fun <T> Flow<T>.timeout(timeoutMillis: Long, emitAfterTimeout: T): Flow<T> { + return flatMapLatest { + flow { + emit(it) + delay(timeoutMillis) + emit(emitAfterTimeout) + } + } + } + + private companion object { + const val DEFAULT_DIALOG_TIMEOUT_MILLIS = 3000 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt index dd5c5d31fae3..b86083abad21 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt @@ -25,7 +25,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.keyboard.data.model.BacklightModel +import com.android.systemui.keyboard.shared.model.BacklightModel import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt index ea15a9f18584..4a32f79285e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt @@ -15,7 +15,7 @@ * */ -package com.android.systemui.keyboard.data.model +package com.android.systemui.keyboard.shared.model /** * Model for current state of keyboard backlight brightness. [level] indicates current level of diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 288f0cc4456d..377a136920f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1929,20 +1929,24 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // If the keyguard is already showing, see if we don't need to bother re-showing it. Check // flags in both files to account for the hiding animation which results in a delay and - // discrepancy between flags. + // discrepancy between flags. If we're in the middle of hiding, do not short circuit so that + // we explicitly re-set state. if (mShowing && mKeyguardStateController.isShowing()) { - if (mPM.isInteractive()) { + if (mPM.isInteractive() && !mHiding) { // It's already showing, and we're not trying to show it while the screen is off. // We can simply reset all of the views. - if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing"); + if (DEBUG) Log.d(TAG, "doKeyguard: not showing (instead, resetting) because it is " + + "already showing, we're interactive, and we were not previously hiding. " + + "It should be safe to short-circuit here."); resetStateLocked(); return; } else { - // We are trying to show the keyguard while the screen is off - this results from - // race conditions involving locking while unlocking. Don't short-circuit here and - // ensure the keyguard is fully re-shown. + // We are trying to show the keyguard while the screen is off or while we were in + // the middle of hiding - this results from race conditions involving locking while + // unlocking. Don't short-circuit here and ensure the keyguard is fully re-shown. Log.e(TAG, - "doKeyguard: already showing, but re-showing since we're not interactive"); + "doKeyguard: already showing, but re-showing because we're interactive or " + + "were in the middle of hiding."); } } @@ -2436,11 +2440,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (DEBUG) Log.d(TAG, "handleShow"); } - mHiding = false; mKeyguardExitAnimationRunner = null; mWakeAndUnlocking = false; setPendingLock(false); - setShowingLocked(true); + + // Force if we we're showing in the middle of hiding, to ensure we end up in the correct + // state. + setShowingLocked(true, mHiding /* force */); + if (mHiding) { + Log.d(TAG, "Forcing setShowingLocked because mHiding=true, which means we're " + + "showing in the middle of hiding."); + } + mHiding = false; + mKeyguardViewControllerLazy.get().show(options); resetKeyguardDonePendingLocked(); mHideAnimationRun = false; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 1fafd7f0a700..2dc8fee25379 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -45,7 +45,6 @@ constructor( override fun start() { listenForOccludedToLockscreen() - listenForOccludedToGone() listenForOccludedToDreaming() listenForOccludedToAodOrDozing() } @@ -73,22 +72,11 @@ constructor( private fun listenForOccludedToLockscreen() { scope.launch { keyguardInteractor.isKeyguardOccluded - .sample( - combine( - keyguardInteractor.isKeyguardShowing, - keyguardTransitionInteractor.startedKeyguardTransitionStep, - ::Pair - ), - ::toTriple - ) - .collect { (isOccluded, isShowing, lastStartedKeyguardState) -> + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { (isOccluded, lastStartedKeyguardState) -> // Occlusion signals come from the framework, and should interrupt any // existing transition - if ( - !isOccluded && - isShowing && - lastStartedKeyguardState.to == KeyguardState.OCCLUDED - ) { + if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) { keyguardTransitionRepository.startTransition( TransitionInfo( name, @@ -102,38 +90,6 @@ constructor( } } - private fun listenForOccludedToGone() { - scope.launch { - keyguardInteractor.isKeyguardOccluded - .sample( - combine( - keyguardInteractor.isKeyguardShowing, - keyguardTransitionInteractor.startedKeyguardTransitionStep, - ::Pair - ), - ::toTriple - ) - .collect { (isOccluded, isShowing, lastStartedKeyguardState) -> - // Occlusion signals come from the framework, and should interrupt any - // existing transition - if ( - !isOccluded && - !isShowing && - lastStartedKeyguardState.to == KeyguardState.OCCLUDED - ) { - keyguardTransitionRepository.startTransition( - TransitionInfo( - name, - KeyguardState.OCCLUDED, - KeyguardState.GONE, - getAnimator(), - ) - ) - } - } - } - } - private fun listenForOccludedToAodOrDozing() { scope.launch { keyguardInteractor.wakefulnessModel diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 097cc3eef304..a31c1e566018 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -32,12 +32,15 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.ColorStateList; +import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Animatable; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; @@ -63,7 +66,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import androidx.appcompat.content.res.AppCompatResources; import androidx.constraintlayout.widget.ConstraintSet; import com.android.internal.annotations.VisibleForTesting; @@ -146,6 +148,12 @@ public class MediaControlPanel { private static final int SMARTSPACE_CARD_CLICK_EVENT = 760; protected static final int SMARTSPACE_CARD_DISMISS_EVENT = 761; + private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f; + private static final float MEDIA_SCRIM_START_ALPHA = 0.25f; + private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f; + private static final float MEDIA_PLAYER_SCRIM_END_ALPHA = 0.9f; + private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f; + private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); // Buttons to show in small player when using semantic actions @@ -779,7 +787,7 @@ public class MediaControlPanel { WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon); if (wallpaperColors != null) { mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT); - artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height); + artwork = addGradientToPlayerAlbum(artworkIcon, mutableColorScheme, width, height); isArtworkBound = true; } else { // If there's no artwork, use colors from the app icon @@ -869,8 +877,9 @@ public class MediaControlPanel { Trace.beginAsyncSection(traceName, traceCookie); // Capture width & height from views in foreground for artwork scaling in background - int width = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredWidth(); - int height = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredHeight(); + int width = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_rec_album_width); + int height = mContext.getResources().getDimensionPixelSize( + R.dimen.qs_media_rec_album_height_expanded); mBackgroundExecutor.execute(() -> { // Album art @@ -880,7 +889,8 @@ public class MediaControlPanel { WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon); if (wallpaperColors != null) { mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT); - artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height); + artwork = addGradientToRecommendationAlbum(artworkIcon, mutableColorScheme, width, + height); } else { artwork = new ColorDrawable(Color.TRANSPARENT); } @@ -889,6 +899,11 @@ public class MediaControlPanel { // Bind the artwork drawable to media cover. ImageView mediaCover = mRecommendationViewHolder.getMediaCoverItems().get(itemIndex); + // Rescale media cover + Matrix coverMatrix = new Matrix(mediaCover.getImageMatrix()); + coverMatrix.postScale(REC_MEDIA_COVER_SCALE_FACTOR, REC_MEDIA_COVER_SCALE_FACTOR, + 0.5f * width, 0.5f * height); + mediaCover.setImageMatrix(coverMatrix); mediaCover.setImageDrawable(artwork); // Set up the app icon. @@ -910,40 +925,62 @@ public class MediaControlPanel { // This method should be called from a background thread. WallpaperColors.fromBitmap takes a // good amount of time. We do that work on the background executor to avoid stalling animations // on the UI Thread. - private WallpaperColors getWallpaperColor(Icon artworkIcon) { + @VisibleForTesting + protected WallpaperColors getWallpaperColor(Icon artworkIcon) { if (artworkIcon != null) { if (artworkIcon.getType() == Icon.TYPE_BITMAP || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { // Avoids extra processing if this is already a valid bitmap - return WallpaperColors - .fromBitmap(artworkIcon.getBitmap()); + Bitmap artworkBitmap = artworkIcon.getBitmap(); + if (artworkBitmap.isRecycled()) { + Log.d(TAG, "Cannot load wallpaper color from a recycled bitmap"); + return null; + } + return WallpaperColors.fromBitmap(artworkBitmap); } else { Drawable artworkDrawable = artworkIcon.loadDrawable(mContext); if (artworkDrawable != null) { - return WallpaperColors - .fromDrawable(artworkIcon.loadDrawable(mContext)); + return WallpaperColors.fromDrawable(artworkDrawable); } } } return null; } - private LayerDrawable addGradientToIcon( - Icon artworkIcon, - ColorScheme mutableColorScheme, - int width, - int height - ) { + @VisibleForTesting + protected LayerDrawable addGradientToPlayerAlbum(Icon artworkIcon, + ColorScheme mutableColorScheme, int width, int height) { Drawable albumArt = getScaledBackground(artworkIcon, width, height); - GradientDrawable gradient = (GradientDrawable) AppCompatResources - .getDrawable(mContext, R.drawable.qs_media_scrim); + GradientDrawable gradient = (GradientDrawable) mContext.getDrawable( + R.drawable.qs_media_scrim).mutate(); + return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme, + MEDIA_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA); + } + + @VisibleForTesting + protected LayerDrawable addGradientToRecommendationAlbum(Icon artworkIcon, + ColorScheme mutableColorScheme, int width, int height) { + // First try scaling rec card using bitmap drawable. + // If returns null, set drawable bounds. + Drawable albumArt = getScaledRecommendationCover(artworkIcon, width, height); + if (albumArt == null) { + albumArt = getScaledBackground(artworkIcon, width, height); + } + GradientDrawable gradient = (GradientDrawable) mContext.getDrawable( + R.drawable.qs_media_rec_scrim).mutate(); + return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme, + MEDIA_REC_SCRIM_START_ALPHA, MEDIA_REC_SCRIM_END_ALPHA); + } + + private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient, + ColorScheme mutableColorScheme, float startAlpha, float endAlpha) { gradient.setColors(new int[] { ColorUtilKt.getColorWithAlpha( MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme), - 0.25f), + startAlpha), ColorUtilKt.getColorWithAlpha( MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme), - 0.9f), + endAlpha), }); return new LayerDrawable(new Drawable[] { albumArt, gradient }); } @@ -1589,6 +1626,29 @@ public class MediaControlPanel { } /** + * Scale artwork to fill the background of media covers in recommendation card. + */ + @UiThread + private Drawable getScaledRecommendationCover(Icon artworkIcon, int width, int height) { + if (width == 0 || height == 0) { + return null; + } + if (artworkIcon != null) { + Bitmap bitmap; + if (artworkIcon.getType() == Icon.TYPE_BITMAP + || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { + Bitmap artworkBitmap = artworkIcon.getBitmap(); + if (artworkBitmap != null) { + bitmap = Bitmap.createScaledBitmap(artworkIcon.getBitmap(), width, + height, false); + return new BitmapDrawable(mContext.getResources(), bitmap); + } + } + } + return null; + } + + /** * Get the current media controller * * @return the controller diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 7855cdfeb4c3..cf5ecdddf854 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -389,6 +389,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp boolean isStrongBiometric) { Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated"); if (mUpdateMonitor.isGoingToSleep()) { + mLogger.deferringAuthenticationDueToSleep(userId, + biometricSourceType, + mPendingAuthenticated != null); mPendingAuthenticated = new PendingAuthenticated(userId, biometricSourceType, isStrongBiometric); Trace.endSection(); @@ -795,6 +798,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp public void onFinishedGoingToSleep() { Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep"); if (mPendingAuthenticated != null) { + mLogger.finishedGoingToSleepWithPendingAuth(); PendingAuthenticated pendingAuthenticated = mPendingAuthenticated; // Post this to make sure it's executed after the device is fully locked. mHandler.post(() -> onBiometricAuthenticated(pendingAuthenticated.userId, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index b88531e59568..ae715b3f20c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -429,7 +429,6 @@ public class DozeParameters implements } dispatchAlwaysOnEvent(); - mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn()); } @Override @@ -469,6 +468,7 @@ public class DozeParameters implements for (Callback callback : mCallbacks) { callback.onAlwaysOnChange(); } + mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn()); } private boolean getPostureSpecificBool( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 726b2344309f..edfc95fcc2e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -16,11 +16,13 @@ package com.android.systemui.statusbar.phone; +import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED; import static android.service.notification.NotificationListenerService.REASON_CLICK; import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.KeyguardManager; import android.app.Notification; import android.app.PendingIntent; @@ -579,8 +581,14 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); mCentralSurfaces.wakeUpForFullScreenIntent(); - fullScreenIntent.send(); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setPendingIntentBackgroundActivityStartMode( + MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + fullScreenIntent.sendAndReturnResult(null, 0, null, null, null, null, + options.toBundle()); entry.notifyFullScreenIntentLaunched(); + mMetricsLogger.count("note_fullscreen", 1); String activityName; diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java index 81ae6e851fb9..c72853ef37be 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java @@ -115,6 +115,17 @@ public abstract class SysUIConcurrencyModule { } /** + * Provide a Long running Executor. + */ + @Provides + @SysUISingleton + @LongRunning + public static DelayableExecutor provideLongRunningDelayableExecutor( + @LongRunning Looper looper) { + return new ExecutorImpl(looper); + } + + /** * Provide a Background-Thread Executor. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 76a01b918952..54b30300ba49 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -41,7 +41,7 @@ import android.view.WindowManager; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -71,17 +71,16 @@ public class ImageWallpaper extends WallpaperService { private HandlerThread mWorker; // used for most tasks (call canvas.drawBitmap, load/unload the bitmap) - @Background - private final DelayableExecutor mBackgroundExecutor; + @LongRunning + private final DelayableExecutor mLongExecutor; // wait at least this duration before unloading the bitmap private static final int DELAY_UNLOAD_BITMAP = 2000; @Inject - public ImageWallpaper(@Background DelayableExecutor backgroundExecutor, - UserTracker userTracker) { + public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) { super(); - mBackgroundExecutor = backgroundExecutor; + mLongExecutor = longExecutor; mUserTracker = userTracker; } @@ -131,7 +130,7 @@ public class ImageWallpaper extends WallpaperService { setFixedSizeAllowed(true); setShowForAllUsers(true); mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor( - mBackgroundExecutor, + mLongExecutor, new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() { @Override public void onColorsProcessed(List<RectF> regions, @@ -230,7 +229,7 @@ public class ImageWallpaper extends WallpaperService { } private void drawFrame() { - mBackgroundExecutor.execute(this::drawFrameSynchronized); + mLongExecutor.execute(this::drawFrameSynchronized); } private void drawFrameSynchronized() { @@ -285,7 +284,7 @@ public class ImageWallpaper extends WallpaperService { } private void unloadBitmapIfNotUsed() { - mBackgroundExecutor.execute(this::unloadBitmapIfNotUsedSynchronized); + mLongExecutor.execute(this::unloadBitmapIfNotUsedSynchronized); } private void unloadBitmapIfNotUsedSynchronized() { @@ -381,7 +380,7 @@ public class ImageWallpaper extends WallpaperService { * - the mini bitmap from color extractor is recomputed * - the DELAY_UNLOAD_BITMAP has passed */ - mBackgroundExecutor.executeDelayed( + mLongExecutor.executeDelayed( this::unloadBitmapIfNotUsedSynchronized, DELAY_UNLOAD_BITMAP); } // even if the bitmap cannot be loaded, call reportEngineShown diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java index 988fd710d2dc..1e8446f8df1d 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java @@ -29,7 +29,7 @@ import android.util.MathUtils; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.util.Assert; import java.io.FileDescriptor; @@ -66,8 +66,8 @@ public class WallpaperLocalColorExtractor { private final List<RectF> mPendingRegions = new ArrayList<>(); private final Set<RectF> mProcessedRegions = new ArraySet<>(); - @Background - private final Executor mBackgroundExecutor; + @LongRunning + private final Executor mLongExecutor; private final WallpaperLocalColorExtractorCallback mWallpaperLocalColorExtractorCallback; @@ -101,13 +101,13 @@ public class WallpaperLocalColorExtractor { /** * Creates a new color extractor. - * @param backgroundExecutor the executor on which the color extraction will be performed + * @param longExecutor the executor on which the color extraction will be performed * @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from * the color extractor. */ - public WallpaperLocalColorExtractor(@Background Executor backgroundExecutor, + public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor, WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) { - mBackgroundExecutor = backgroundExecutor; + mLongExecutor = longExecutor; mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback; } @@ -117,7 +117,7 @@ public class WallpaperLocalColorExtractor { * not recomputed. */ public void setDisplayDimensions(int displayWidth, int displayHeight) { - mBackgroundExecutor.execute(() -> + mLongExecutor.execute(() -> setDisplayDimensionsSynchronized(displayWidth, displayHeight)); } @@ -144,7 +144,7 @@ public class WallpaperLocalColorExtractor { * @param bitmap the new wallpaper */ public void onBitmapChanged(@NonNull Bitmap bitmap) { - mBackgroundExecutor.execute(() -> onBitmapChangedSynchronized(bitmap)); + mLongExecutor.execute(() -> onBitmapChangedSynchronized(bitmap)); } private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) { @@ -167,7 +167,7 @@ public class WallpaperLocalColorExtractor { * @param pages the total number of pages of the launcher */ public void onPageChanged(int pages) { - mBackgroundExecutor.execute(() -> onPageChangedSynchronized(pages)); + mLongExecutor.execute(() -> onPageChangedSynchronized(pages)); } private void onPageChangedSynchronized(int pages) { @@ -194,7 +194,7 @@ public class WallpaperLocalColorExtractor { */ public void addLocalColorsAreas(@NonNull List<RectF> regions) { if (regions.size() > 0) { - mBackgroundExecutor.execute(() -> addLocalColorsAreasSynchronized(regions)); + mLongExecutor.execute(() -> addLocalColorsAreasSynchronized(regions)); } else { Log.w(TAG, "Attempt to add colors with an empty list"); } @@ -218,7 +218,7 @@ public class WallpaperLocalColorExtractor { * @param regions The areas of interest in our wallpaper (in screen pixel coordinates) */ public void removeLocalColorAreas(@NonNull List<RectF> regions) { - mBackgroundExecutor.execute(() -> removeLocalColorAreasSynchronized(regions)); + mLongExecutor.execute(() -> removeLocalColorAreasSynchronized(regions)); } private void removeLocalColorAreasSynchronized(@NonNull List<RectF> regions) { @@ -236,7 +236,7 @@ public class WallpaperLocalColorExtractor { * Clean up the memory (in particular, the mini bitmap) used by this class. */ public void cleanUp() { - mBackgroundExecutor.execute(this::cleanUpSynchronized); + mLongExecutor.execute(this::cleanUpSynchronized); } private void cleanUpSynchronized() { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 9600fd88a2a3..08f7eaee3011 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -25,7 +25,6 @@ import static android.service.notification.NotificationListenerService.REASON_GR import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; -import static com.android.systemui.flags.Flags.WM_BUBBLE_BAR; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -364,7 +363,6 @@ public class BubblesManager { }); } }; - mBubbles.setBubbleBarEnabled(featureFlags.isEnabled(WM_BUBBLE_BAR)); mBubbles.setSysuiProxy(mSysuiProxy); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index b765ab3c5eac..a245c01d74de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -25,7 +25,9 @@ import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.keyguard.logging.KeyguardLogger import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -109,6 +111,7 @@ class AuthRippleControllerTest : SysuiTestCase() { udfpsControllerProvider, statusBarStateController, featureFlags, + KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), rippleView ) controller.init() diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index faa5db4327a6..ab6d5b771d5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -94,7 +94,9 @@ public class DistanceClassifierTest extends ClassifierTest { mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3)); mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300)); mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301)); - mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); + mClassifier.onTouchEvent(appendMoveEvent(1, 19, 501)); + mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); //event will be dropped + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index 2edc3d361316..8eadadff1ca5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -75,16 +75,17 @@ public class FalsingDataProviderTest extends ClassifierTest { } @Test - public void test_trackMotionEvents() { + public void test_trackMotionEvents_dropUpEvent() { mDataProvider.onMotionEvent(appendDownEvent(2, 9)); mDataProvider.onMotionEvent(appendMoveEvent(4, 7)); - mDataProvider.onMotionEvent(appendUpEvent(6, 5)); + mDataProvider.onMotionEvent(appendMoveEvent(6, 5)); + mDataProvider.onMotionEvent(appendUpEvent(0, 0)); // event will be dropped List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents(); assertThat(motionEventList.size()).isEqualTo(3); assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN); assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE); - assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP); + assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE); assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L); assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L); assertThat(motionEventList.get(2).getEventTime()).isEqualTo(3L); @@ -97,6 +98,28 @@ public class FalsingDataProviderTest extends ClassifierTest { } @Test + public void test_trackMotionEvents_keepUpEvent() { + mDataProvider.onMotionEvent(appendDownEvent(2, 9)); + mDataProvider.onMotionEvent(appendMoveEvent(4, 7)); + mDataProvider.onMotionEvent(appendUpEvent(0, 0, 100)); + List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents(); + + assertThat(motionEventList.size()).isEqualTo(3); + assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN); + assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE); + assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP); + assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L); + assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L); + assertThat(motionEventList.get(2).getEventTime()).isEqualTo(100); + assertThat(motionEventList.get(0).getX()).isEqualTo(2f); + assertThat(motionEventList.get(1).getX()).isEqualTo(4f); + assertThat(motionEventList.get(2).getX()).isEqualTo(0f); + assertThat(motionEventList.get(0).getY()).isEqualTo(9f); + assertThat(motionEventList.get(1).getY()).isEqualTo(7f); + assertThat(motionEventList.get(2).getY()).isEqualTo(0f); + } + + @Test public void test_trackRecentMotionEvents() { mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1)); mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index c343c20398e9..ae2b8bbb4ce6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -68,6 +68,15 @@ public class ZigZagClassifierTest extends ClassifierTest { } @Test + public void testPass_dropClosingUpEvent() { + appendMoveEvent(0, 0); + appendMoveEvent(0, 100); + appendMoveEvent(0, 200); + appendUpEvent(0, 180); // this event would push us over the maxDevianceY + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); + } + + @Test public void testPass_fewTouchesHorizontal() { assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); appendMoveEvent(0, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index 2099281d694a..c2fb904f64ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -446,7 +446,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true); when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString())) .thenReturn(true); - when(mClipboardUtils.getAction(any(CharSequence.class), any(TextLinks.class), anyString())) + when(mClipboardUtils.getAction(any(TextLinks.class), anyString())) .thenReturn(Optional.of(Mockito.mock(RemoteAction.class))); when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() { @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java index aea6be3d468b..3d8f04e08825 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.when; @@ -77,6 +78,74 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { @Test public void test_getAction_noLinks_returnsEmptyOptional() { + Optional<RemoteAction> action = + mClipboardUtils.getAction(Mockito.mock(TextLinks.class), "abc"); + + assertTrue(action.isEmpty()); + } + + @Test + public void test_getAction_returnsFirstLink() { + TextLinks links = getFakeTextLinksBuilder().build(); + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( + classificationA, classificationB); + + RemoteAction result = mClipboardUtils.getAction(links, "test").orElse(null); + + assertEquals(actionA, result); + } + + @Test + public void test_getAction_skipsMatchingComponent() { + TextLinks links = getFakeTextLinksBuilder().build(); + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( + classificationA, classificationB); + + RemoteAction result = mClipboardUtils.getAction(links, "abc").orElse(null); + + assertEquals(actionB, result); + } + + @Test + public void test_getAction_skipsShortEntity() { + TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22"); + final Map<String, Float> scores = new ArrayMap<>(); + scores.put(TextClassifier.TYPE_EMAIL, 1f); + textLinks.addLink(20, 22, scores); + textLinks.addLink(0, 22, scores); + + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn( + classificationA); + when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn( + classificationB); + + RemoteAction result = mClipboardUtils.getAction(textLinks.build(), "test").orElse(null); + + assertEquals(actionB, result); + } + + // TODO(b/267162944): Next four tests (marked "legacy") are obsolete once + // CLIPBOARD_MINIMIZED_LAYOUT flag is released and removed + @Test + public void test_getAction_noLinks_returnsEmptyOptional_legacy() { ClipData.Item item = new ClipData.Item("no text links"); item.setTextLinks(Mockito.mock(TextLinks.class)); @@ -86,8 +155,8 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { } @Test - public void test_getAction_returnsFirstLink() { - when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks()); + public void test_getAction_returnsFirstLink_legacy() { + when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build()); when(mClipDataItem.getText()).thenReturn(""); RemoteAction actionA = constructRemoteAction("abc"); RemoteAction actionB = constructRemoteAction("def"); @@ -98,14 +167,14 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( classificationA, classificationB); - RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "def").orElse(null); + RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null); assertEquals(actionA, result); } @Test - public void test_getAction_skipsMatchingComponent() { - when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks()); + public void test_getAction_skipsMatchingComponent_legacy() { + when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build()); when(mClipDataItem.getText()).thenReturn(""); RemoteAction actionA = constructRemoteAction("abc"); RemoteAction actionB = constructRemoteAction("def"); @@ -122,6 +191,33 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { } @Test + public void test_getAction_skipsShortEntity_legacy() { + TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22"); + final Map<String, Float> scores = new ArrayMap<>(); + scores.put(TextClassifier.TYPE_EMAIL, 1f); + textLinks.addLink(20, 22, scores); + textLinks.addLink(0, 22, scores); + + when(mClipDataItem.getTextLinks()).thenReturn(textLinks.build()); + when(mClipDataItem.getText()).thenReturn(textLinks.build().getText()); + + RemoteAction actionA = constructRemoteAction("abc"); + RemoteAction actionB = constructRemoteAction("def"); + TextClassification classificationA = Mockito.mock(TextClassification.class); + when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); + TextClassification classificationB = Mockito.mock(TextClassification.class); + when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); + when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn( + classificationA); + when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn( + classificationB); + + RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null); + + assertEquals(actionB, result); + } + + @Test public void test_extra_withPackage_returnsTrue() { PersistableBundle b = new PersistableBundle(); b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true); @@ -184,12 +280,12 @@ public class ClipboardOverlayUtilsTest extends SysuiTestCase { return action; } - private static TextLinks getFakeTextLinks() { - TextLinks.Builder textLinks = new TextLinks.Builder("test"); + private static TextLinks.Builder getFakeTextLinksBuilder() { + TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22"); final Map<String, Float> scores = new ArrayMap<>(); scores.put(TextClassifier.TYPE_EMAIL, 1f); - textLinks.addLink(0, 0, scores); - textLinks.addLink(0, 0, scores); - return textLinks.build(); + textLinks.addLink(0, 22, scores); + textLinks.addLink(0, 22, scores); + return textLinks; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt new file mode 100644 index 000000000000..ec94cdec78f0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository +import com.android.systemui.keyboard.shared.model.BacklightModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class KeyboardBacklightInteractorTest : SysuiTestCase() { + + private val keyboardRepository = FakeKeyboardRepository() + private lateinit var underTest: KeyboardBacklightInteractor + + @Before + fun setUp() { + underTest = KeyboardBacklightInteractor(keyboardRepository) + } + + @Test + fun emitsNull_whenKeyboardJustConnected() = runTest { + val latest by collectLastValue(underTest.backlight) + keyboardRepository.setKeyboardConnected(true) + + assertThat(latest).isNull() + } + + @Test + fun emitsBacklight_whenKeyboardConnectedAndBacklightChanged() = runTest { + keyboardRepository.setKeyboardConnected(true) + keyboardRepository.setBacklight(BacklightModel(1, 5)) + + assertThat(underTest.backlight.first()).isEqualTo(BacklightModel(1, 5)) + } + + @Test + fun emitsNull_afterKeyboardDisconnecting() = runTest { + val latest by collectLastValue(underTest.backlight) + keyboardRepository.setKeyboardConnected(true) + keyboardRepository.setBacklight(BacklightModel(1, 5)) + + keyboardRepository.setKeyboardConnected(false) + + assertThat(latest).isNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt new file mode 100644 index 000000000000..ec05d10b793c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor +import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository +import com.android.systemui.keyboard.shared.model.BacklightModel +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class BacklightDialogViewModelTest : SysuiTestCase() { + + private val keyboardRepository = FakeKeyboardRepository() + private lateinit var underTest: BacklightDialogViewModel + @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper + private val timeoutMillis = 3000L + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any())) + .thenReturn(timeoutMillis.toInt()) + underTest = + BacklightDialogViewModel( + KeyboardBacklightInteractor(keyboardRepository), + accessibilityManagerWrapper + ) + keyboardRepository.setKeyboardConnected(true) + } + + @Test + fun emitsViewModel_whenBacklightChanged() = runTest { + keyboardRepository.setBacklight(BacklightModel(1, 5)) + + assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5)) + } + + @Test + fun emitsNull_afterTimeout() = runTest { + val latest by collectLastValue(underTest.dialogContent) + keyboardRepository.setBacklight(BacklightModel(1, 5)) + + assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5)) + advanceTimeBy(timeoutMillis + 1) + assertThat(latest).isNull() + } + + @Test + fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest { + val latest by collectLastValue(underTest.dialogContent) + keyboardRepository.setKeyboardConnected(true) + + keyboardRepository.setBacklight(BacklightModel(1, 5)) + assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5)) + + advanceTimeBy(timeoutMillis * 2 / 3) + // timeout yet to pass, no new emission + keyboardRepository.setBacklight(BacklightModel(2, 5)) + assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5)) + + advanceTimeBy(timeoutMillis * 2 / 3) + // timeout refreshed because of last `setBacklight`, still content present + assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5)) + + advanceTimeBy(timeoutMillis * 2 / 3) + // finally timeout reached and null emitted + assertThat(latest).isNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 2209852df0c0..fe9098fa5c25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -358,92 +358,6 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `OCCLUDED to GONE`() = - testScope.runTest { - // GIVEN a device on lockscreen - keyguardRepository.setKeyguardShowing(true) - runCurrent() - - // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - keyguardRepository.setKeyguardOccluded(true) - runCurrent() - reset(mockTransitionRepository) - - // WHEN keyguard goes away - keyguardRepository.setKeyguardShowing(false) - // AND occlusion ends - keyguardRepository.setKeyguardOccluded(false) - runCurrent() - - val info = - withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) - } - // THEN a transition to GONE should occur - assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") - assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED) - assertThat(info.to).isEqualTo(KeyguardState.GONE) - assertThat(info.animator).isNotNull() - - coroutineContext.cancelChildren() - } - - @Test - fun `OCCLUDED to LOCKSCREEN`() = - testScope.runTest { - // GIVEN a device on lockscreen - keyguardRepository.setKeyguardShowing(true) - runCurrent() - - // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - keyguardRepository.setKeyguardOccluded(true) - runCurrent() - reset(mockTransitionRepository) - - // WHEN occlusion ends - keyguardRepository.setKeyguardOccluded(false) - runCurrent() - - val info = - withArgCaptor<TransitionInfo> { - verify(mockTransitionRepository).startTransition(capture(), anyBoolean()) - } - // THEN a transition to LOCKSCREEN should occur - assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") - assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED) - assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN) - assertThat(info.animator).isNotNull() - - coroutineContext.cancelChildren() - } - - @Test fun `LOCKSCREEN to DOZING`() = testScope.runTest { // GIVEN a device with AOD not available diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index 55a33b6636e0..fd353afff7c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -27,6 +27,7 @@ import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color +import android.graphics.Matrix import android.graphics.drawable.Animatable2 import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable @@ -78,6 +79,8 @@ import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.media.dialog.MediaOutputDialogFactory +import com.android.systemui.monet.ColorScheme +import com.android.systemui.monet.Style import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -214,6 +217,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var recSubtitleMock2: TextView @Mock private lateinit var recSubtitleMock3: TextView @Mock private lateinit var coverItem: ImageView + @Mock private lateinit var matrix: Matrix private lateinit var coverItem1: ImageView private lateinit var coverItem2: ImageView private lateinit var coverItem3: ImageView @@ -700,6 +704,46 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun addTwoPlayerGradients_differentStates() { + // Setup redArtwork and its color scheme. + val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val redCanvas = Canvas(redBmp) + redCanvas.drawColor(Color.RED) + val redArt = Icon.createWithBitmap(redBmp) + val redWallpaperColor = player.getWallpaperColor(redArt) + val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT) + + // Setup greenArt and its color scheme. + val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val greenCanvas = Canvas(greenBmp) + greenCanvas.drawColor(Color.GREEN) + val greenArt = Icon.createWithBitmap(greenBmp) + val greenWallpaperColor = player.getWallpaperColor(greenArt) + val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT) + + // Add gradient to both icons. + val redArtwork = player.addGradientToPlayerAlbum(redArt, redColorScheme, 10, 10) + val greenArtwork = player.addGradientToPlayerAlbum(greenArt, greenColorScheme, 10, 10) + + // They should have different constant states as they have different gradient color. + assertThat(redArtwork.getDrawable(1).constantState) + .isNotEqualTo(greenArtwork.getDrawable(1).constantState) + } + + @Test + fun getWallpaperColor_recycledBitmap_notCrashing() { + // Setup redArt icon. + val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val redArt = Icon.createWithBitmap(redBmp) + + // Recycle bitmap of redArt icon. + redArt.bitmap.recycle() + + // get wallpaperColor without illegal exception. + player.getWallpaperColor(redArt) + } + + @Test fun bind_seekBarDisabled_hasActions_seekBarVisibilityIsSetToInvisible() { useRealConstraintSets() @@ -2092,6 +2136,7 @@ public class MediaControlPanelTest : SysuiTestCase() { .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3)) whenever(recommendationViewHolder.mediaSubtitles) .thenReturn(listOf(recSubtitleMock1, recSubtitleMock2, recSubtitleMock3)) + whenever(coverItem.imageMatrix).thenReturn(matrix) val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) val canvas = Canvas(bmp) @@ -2127,6 +2172,7 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(recCardTitle).setTextColor(any<Int>()) verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java)) verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java)) + verify(coverItem, times(3)).imageMatrix = any() } @Test @@ -2189,6 +2235,34 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun addTwoRecommendationGradients_differentStates() { + // Setup redArtwork and its color scheme. + val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val redCanvas = Canvas(redBmp) + redCanvas.drawColor(Color.RED) + val redArt = Icon.createWithBitmap(redBmp) + val redWallpaperColor = player.getWallpaperColor(redArt) + val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT) + + // Setup greenArt and its color scheme. + val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val greenCanvas = Canvas(greenBmp) + greenCanvas.drawColor(Color.GREEN) + val greenArt = Icon.createWithBitmap(greenBmp) + val greenWallpaperColor = player.getWallpaperColor(greenArt) + val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT) + + // Add gradient to both icons. + val redArtwork = player.addGradientToRecommendationAlbum(redArt, redColorScheme, 10, 10) + val greenArtwork = + player.addGradientToRecommendationAlbum(greenArt, greenColorScheme, 10, 10) + + // They should have different constant states as they have different gradient color. + assertThat(redArtwork.getDrawable(1).constantState) + .isNotEqualTo(greenArtwork.getDrawable(1).constantState) + } + + @Test fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() { fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true) val semanticActions = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index eb5edbc21d89..f5b7ca804fbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -180,6 +180,7 @@ public class DozeParametersTest extends SysuiTestCase { when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1"); + verify(mScreenOffAnimationController).onAlwaysOnChanged(false); assertThat(mDozeParameters.getAlwaysOn()).isFalse(); } @@ -196,13 +197,16 @@ public class DozeParametersTest extends SysuiTestCase { mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true); verify(callback, times(2)).onAlwaysOnChange(); + verify(mScreenOffAnimationController, times(2)).onAlwaysOnChanged(false); assertThat(mDozeParameters.getAlwaysOn()).isFalse(); + reset(mScreenOffAnimationController); reset(callback); when(mBatteryController.isAodPowerSave()).thenReturn(false); mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true); verify(callback).onAlwaysOnChange(); + verify(mScreenOffAnimationController).onAlwaysOnChanged(true); assertThat(mDozeParameters.getAlwaysOn()).isTrue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt index 85cfef727954..fd368eb07b5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt @@ -16,22 +16,24 @@ package com.android.systemui.unfold.updates +import android.content.Context +import android.hardware.display.DisplayManager +import android.os.Looper import android.testing.AndroidTestingRunner -import android.view.IRotationWatcher -import android.view.IWindowManager +import android.view.Display import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.utils.os.FakeHandler import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @@ -42,19 +44,23 @@ class RotationChangeProviderTest : SysuiTestCase() { private lateinit var rotationChangeProvider: RotationChangeProvider - @Mock lateinit var windowManagerInterface: IWindowManager + @Mock lateinit var displayManager: DisplayManager @Mock lateinit var listener: RotationListener - @Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher> - private val fakeExecutor = FakeExecutor(FakeSystemClock()) + @Mock lateinit var display: Display + @Captor lateinit var displayListener: ArgumentCaptor<DisplayManager.DisplayListener> + private val fakeHandler = FakeHandler(Looper.getMainLooper()) + + private lateinit var spyContext: Context @Before fun setup() { MockitoAnnotations.initMocks(this) - rotationChangeProvider = - RotationChangeProvider(windowManagerInterface, context, fakeExecutor) + spyContext = spy(context) + whenever(spyContext.display).thenReturn(display) + rotationChangeProvider = RotationChangeProvider(displayManager, spyContext, fakeHandler) rotationChangeProvider.addCallback(listener) - fakeExecutor.runAllReady() - verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt()) + fakeHandler.dispatchQueuedMessages() + verify(displayManager).registerDisplayListener(displayListener.capture(), any()) } @Test @@ -70,15 +76,16 @@ class RotationChangeProviderTest : SysuiTestCase() { verify(listener).onRotationChanged(42) rotationChangeProvider.removeCallback(listener) - fakeExecutor.runAllReady() + fakeHandler.dispatchQueuedMessages() sendRotationUpdate(43) - verify(windowManagerInterface).removeRotationWatcher(any()) + verify(displayManager).unregisterDisplayListener(any()) verifyNoMoreInteractions(listener) } private fun sendRotationUpdate(newRotation: Int) { - rotationWatcher.value.onRotationChanged(newRotation) - fakeExecutor.runAllReady() + whenever(display.rotation).thenReturn(newRotation) + displayListener.allValues.forEach { it.onDisplayChanged(display.displayId) } + fakeHandler.dispatchQueuedMessages() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java index 31cce4f3168b..468c5a73645b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java @@ -88,7 +88,7 @@ public class ImageWallpaperTest extends SysuiTestCase { @Mock private Bitmap mWallpaperBitmap; FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock); + FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); @Before public void setUp() throws Exception { @@ -125,7 +125,7 @@ public class ImageWallpaperTest extends SysuiTestCase { @Test public void testBitmapWallpaper_normal() { - // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH. + // Will use an image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH. // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH. int bitmapSide = DISPLAY_WIDTH; testSurfaceHelper( @@ -137,7 +137,7 @@ public class ImageWallpaperTest extends SysuiTestCase { @Test public void testBitmapWallpaper_low_resolution() { - // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT. + // Will use an image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT. // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT. testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */, LOW_BMP_HEIGHT /* bitmapHeight */, @@ -161,13 +161,13 @@ public class ImageWallpaperTest extends SysuiTestCase { ImageWallpaper.CanvasEngine spyEngine = getSpyEngine(); spyEngine.onCreate(mSurfaceHolder); spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder); - assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1); + assertThat(mFakeExecutor.numPending()).isAtLeast(1); int n = 0; - while (mFakeBackgroundExecutor.numPending() >= 1) { + while (mFakeExecutor.numPending() >= 1) { n++; assertThat(n).isAtMost(10); - mFakeBackgroundExecutor.runNextReady(); + mFakeExecutor.runNextReady(); mFakeSystemClock.advanceTime(1000); } @@ -176,7 +176,7 @@ public class ImageWallpaperTest extends SysuiTestCase { } private ImageWallpaper createImageWallpaper() { - return new ImageWallpaper(mFakeBackgroundExecutor, mUserTracker) { + return new ImageWallpaper(mFakeExecutor, mUserTracker) { @Override public Engine onCreateEngine() { return new CanvasEngine() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt new file mode 100644 index 000000000000..4e435462be50 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.data.repository + +import com.android.systemui.keyboard.shared.model.BacklightModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull + +class FakeKeyboardRepository : KeyboardRepository { + + private val _keyboardConnected = MutableStateFlow(false) + override val keyboardConnected: Flow<Boolean> = _keyboardConnected + + private val _backlightState: MutableStateFlow<BacklightModel?> = MutableStateFlow(null) + // filtering to make sure backlight doesn't have default initial value + override val backlight: Flow<BacklightModel> = _backlightState.filterNotNull() + + fun setBacklight(state: BacklightModel) { + _backlightState.value = state + } + + fun setKeyboardConnected(connected: Boolean) { + _keyboardConnected.value = connected + } +} diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp index 180b611aa13b..2e0a9462ffbe 100644 --- a/packages/SystemUI/unfold/Android.bp +++ b/packages/SystemUI/unfold/Android.bp @@ -35,6 +35,7 @@ android_library { ], kotlincflags: ["-Xjvm-default=enable"], java_version: "1.8", + sdk_version: "current", min_sdk_version: "current", plugins: ["dagger2-compiler"], } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt index 068347cfe9d8..a07966823c81 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt @@ -19,8 +19,8 @@ package com.android.systemui.unfold import android.content.ContentResolver import android.content.Context import android.hardware.SensorManager +import android.hardware.display.DisplayManager import android.os.Handler -import android.view.IWindowManager import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg @@ -61,7 +61,7 @@ interface UnfoldSharedComponent { @BindsInstance @UnfoldMain executor: Executor, @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor, @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, - @BindsInstance windowManager: IWindowManager, + @BindsInstance displayManager: DisplayManager, @BindsInstance contentResolver: ContentResolver = context.contentResolver ): UnfoldSharedComponent } @@ -84,8 +84,9 @@ interface RemoteUnfoldSharedComponent { @BindsInstance context: Context, @BindsInstance config: UnfoldTransitionConfig, @BindsInstance @UnfoldMain executor: Executor, + @BindsInstance @UnfoldMain handler: Handler, @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor, - @BindsInstance windowManager: IWindowManager, + @BindsInstance displayManager: DisplayManager, @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, ): RemoteUnfoldSharedComponent } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index 8eb79df55496..18399194434a 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -19,8 +19,8 @@ package com.android.systemui.unfold import android.content.Context import android.hardware.SensorManager +import android.hardware.display.DisplayManager import android.os.Handler -import android.view.IWindowManager import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider @@ -47,7 +47,7 @@ fun createUnfoldSharedComponent( mainExecutor: Executor, singleThreadBgExecutor: Executor, tracingTagPrefix: String, - windowManager: IWindowManager, + displayManager: DisplayManager, ): UnfoldSharedComponent = DaggerUnfoldSharedComponent.factory() .create( @@ -61,7 +61,7 @@ fun createUnfoldSharedComponent( mainExecutor, singleThreadBgExecutor, tracingTagPrefix, - windowManager, + displayManager, ) /** @@ -73,16 +73,18 @@ fun createRemoteUnfoldSharedComponent( context: Context, config: UnfoldTransitionConfig, mainExecutor: Executor, + mainHandler: Handler, singleThreadBgExecutor: Executor, tracingTagPrefix: String, - windowManager: IWindowManager, + displayManager: DisplayManager, ): RemoteUnfoldSharedComponent = DaggerRemoteUnfoldSharedComponent.factory() .create( context, config, mainExecutor, + mainHandler, singleThreadBgExecutor, - windowManager, + displayManager, tracingTagPrefix, ) diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index d19b414cb963..28e493651137 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -16,7 +16,6 @@ package com.android.systemui.unfold.progress import android.os.Trace -import android.os.Trace.TRACE_TAG_APP import android.util.Log import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat @@ -110,7 +109,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( if (DEBUG) { Log.d(TAG, "onFoldUpdate = ${update.name()}") - Trace.traceCounter(Trace.TRACE_TAG_APP, "fold_update", update) + Trace.setCounter("fold_update", update.toLong()) } } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 82fd2258120a..d653fc7beff2 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -119,7 +119,7 @@ constructor( "lastHingeAngle: $lastHingeAngle, " + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" ) - Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt()) + Trace.setCounter( "hinge_angle", angle.toLong()) } val currentDirection = diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt index 0cf8224d3a3f..ce8f1a178d05 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt @@ -17,36 +17,32 @@ package com.android.systemui.unfold.updates import android.content.Context +import android.hardware.display.DisplayManager +import android.os.Handler import android.os.RemoteException -import android.view.IRotationWatcher -import android.view.IWindowManager -import android.view.Surface.Rotation import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.util.CallbackController -import java.util.concurrent.Executor import javax.inject.Inject /** - * Allows to subscribe to rotation changes. - * - * This is needed as rotation updates from [IWindowManager] are received in a binder thread, while - * most of the times we want them in the main one. Updates are provided for the display associated + * Allows to subscribe to rotation changes. Updates are provided for the display associated * to [context]. */ class RotationChangeProvider @Inject constructor( - private val windowManagerInterface: IWindowManager, + private val displayManager: DisplayManager, private val context: Context, - @UnfoldMain private val mainExecutor: Executor, + @UnfoldMain private val mainHandler: Handler, ) : CallbackController<RotationChangeProvider.RotationListener> { private val listeners = mutableListOf<RotationListener>() - private val rotationWatcher = RotationWatcher() + private val displayListener = RotationDisplayListener() + private var lastRotation: Int? = null override fun addCallback(listener: RotationListener) { - mainExecutor.execute { + mainHandler.post { if (listeners.isEmpty()) { subscribeToRotation() } @@ -55,17 +51,18 @@ constructor( } override fun removeCallback(listener: RotationListener) { - mainExecutor.execute { + mainHandler.post { listeners -= listener if (listeners.isEmpty()) { unsubscribeToRotation() + lastRotation = null } } } private fun subscribeToRotation() { try { - windowManagerInterface.watchRotation(rotationWatcher, context.displayId) + displayManager.registerDisplayListener(displayListener, mainHandler) } catch (e: RemoteException) { throw e.rethrowFromSystemServer() } @@ -73,7 +70,7 @@ constructor( private fun unsubscribeToRotation() { try { - windowManagerInterface.removeRotationWatcher(rotationWatcher) + displayManager.unregisterDisplayListener(displayListener) } catch (e: RemoteException) { throw e.rethrowFromSystemServer() } @@ -82,12 +79,25 @@ constructor( /** Gets notified of rotation changes. */ fun interface RotationListener { /** Called once rotation changes. */ - fun onRotationChanged(@Rotation newRotation: Int) + fun onRotationChanged(newRotation: Int) } - private inner class RotationWatcher : IRotationWatcher.Stub() { - override fun onRotationChanged(rotation: Int) { - mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } } + private inner class RotationDisplayListener : DisplayManager.DisplayListener { + + override fun onDisplayChanged(displayId: Int) { + val display = context.display ?: return + + if (displayId == display.displayId) { + val currentRotation = display.rotation + if (lastRotation == null || lastRotation != currentRotation) { + listeners.forEach { it.onRotationChanged(currentRotation) } + lastRotation = currentRotation + } + } } + + override fun onDisplayAdded(displayId: Int) {} + + override fun onDisplayRemoved(displayId: Int) {} } } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt index 06ca153b694b..ce5c5f91914b 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt @@ -79,10 +79,9 @@ constructor( companion object { fun ContentResolver.areAnimationsEnabled(): Boolean { val animationScale = - Settings.Global.getStringForUser( + Settings.Global.getString( this, Settings.Global.ANIMATOR_DURATION_SCALE, - this.userId ) ?.toFloatOrNull() ?: 1f diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java index 864fe0f5edc1..7df0d861dc22 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -99,6 +99,9 @@ public class SensorController { private int createSensorInternal(IBinder sensorToken, VirtualSensorConfig config) throws SensorCreationException { + if (config.getType() <= 0) { + throw new SensorCreationException("Received an invalid virtual sensor type."); + } final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId, config.getType(), config.getName(), config.getVendor() == null ? "" : config.getVendor(), config.getFlags(), diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index e3f00ded026f..9e95e5fb0a40 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -995,7 +995,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = "enable_wait_for_finish_attach_application"; - private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true; + private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false; /** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */ public volatile boolean mEnableWaitForFinishAttachApplication = diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1960c330cc73..4ba6854b9234 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -101,7 +101,7 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS; @@ -5588,8 +5588,11 @@ public class ActivityManagerService extends IActivityManager.Stub boolean isChangeEnabled = CompatChanges.isChangeEnabled( PendingIntent.BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT, owningUid); - logUnsafeMutableImplicitPi(packageName, resolvedTypes, owningUid, i, intent, - isChangeEnabled); + String resolvedType = resolvedTypes == null + || i >= resolvedTypes.length ? null : resolvedTypes[i]; + ActivityManagerUtils.logUnsafeIntentEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED, + owningUid, intent, resolvedType, isChangeEnabled); if (isChangeEnabled) { String msg = packageName + ": Targeting U+ (version " + Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows" @@ -5655,24 +5658,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private void logUnsafeMutableImplicitPi(String packageName, String[] resolvedTypes, - int owningUid, int i, Intent intent, boolean isChangeEnabled) { - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - String resolvedType = resolvedTypes == null || i >= resolvedTypes.length ? null - : resolvedTypes[i]; - FrameworkStatsLog.write(UNSAFE_INTENT_EVENT_REPORTED, - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED, - owningUid, - null, - packageName, - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - isChangeEnabled); - } - @Override public int sendIntentSender(IApplicationThread caller, IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, @@ -12912,18 +12897,9 @@ public class ActivityManagerService extends IActivityManager.Stub boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid( ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, callingUid); - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - FrameworkStatsLog.write(UNSAFE_INTENT_EVENT_REPORTED, - FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, - callingUid, - componentInfo, - callerPackage, - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - hasToBeExportedToMatch); + ActivityManagerUtils.logUnsafeIntentEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, + callingUid, intent, resolvedType, hasToBeExportedToMatch); if (!hasToBeExportedToMatch) { return; } diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java index 9be553c49a35..01466b845a61 100644 --- a/services/core/java/com/android/server/am/ActivityManagerUtils.java +++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java @@ -17,11 +17,13 @@ package com.android.server.am; import android.app.ActivityThread; import android.content.ContentResolver; +import android.content.Intent; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -133,4 +135,25 @@ public class ActivityManagerUtils { public static int hashComponentNameForAtom(String shortInstanceName) { return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash(); } + + /** + * Helper method to log an unsafe intent event. + */ + public static void logUnsafeIntentEvent(int event, int callingUid, + Intent intent, String resolvedType, boolean blocked) { + String[] categories = intent.getCategories() == null ? new String[0] + : intent.getCategories().toArray(String[]::new); + String component = intent.getComponent() == null ? null + : intent.getComponent().flattenToString(); + FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, + event, + callingUid, + component, + intent.getPackage(), + intent.getAction(), + categories, + resolvedType, + intent.getScheme(), + blocked); + } } diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java index 42be95b7377a..ecb7e7ca08fb 100644 --- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java +++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java @@ -20,8 +20,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.common.AuthenticateReason; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; +import android.hardware.biometrics.common.WakeReason; +import android.hardware.face.FaceAuthenticateOptions; +import android.hardware.fingerprint.FingerprintAuthenticateOptions; +import android.os.PowerManager; import android.view.Surface; /** @@ -50,12 +55,127 @@ public class OperationContextExt { mAidlContext = context; } - /** Gets the subset of the context that can be shared with the HAL. */ + /** + * Gets the subset of the context that can be shared with the HAL. + * + * When starting a new operation use methods like to update & fetch the context: + * <ul> + * <li>{@link #toAidlContext(FaceAuthenticateOptions)} + * <li>{@link #toAidlContext(FingerprintAuthenticateOptions)} + * </ul> + * + * Use this method for any subsequent calls to the HAL or for operations that do + * not accept any options. + * + * @return the underlying AIDL context + */ @NonNull public OperationContext toAidlContext() { return mAidlContext; } + /** + * Gets the subset of the context that can be shared with the HAL and updates + * it with the given options. + * + * @param options authenticate options + * @return the underlying AIDL context + */ + @NonNull + public OperationContext toAidlContext(@NonNull FaceAuthenticateOptions options) { + mAidlContext.authenticateReason = AuthenticateReason + .faceAuthenticateReason(getAuthReason(options)); + mAidlContext.wakeReason = getWakeReason(options); + + return mAidlContext; + } + + /** + * Gets the subset of the context that can be shared with the HAL and updates + * it with the given options. + * + * @param options authenticate options + * @return the underlying AIDL context + */ + @NonNull + public OperationContext toAidlContext(@NonNull FingerprintAuthenticateOptions options) { + mAidlContext.authenticateReason = AuthenticateReason + .fingerprintAuthenticateReason(getAuthReason(options)); + mAidlContext.wakeReason = getWakeReason(options); + + return mAidlContext; + } + + @AuthenticateReason.Face + private int getAuthReason(@NonNull FaceAuthenticateOptions options) { + switch (options.getAuthenticateReason()) { + case FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP: + return AuthenticateReason.Face.STARTED_WAKING_UP; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN: + return AuthenticateReason.Face.PRIMARY_BOUNCER_SHOWN; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE: + return AuthenticateReason.Face.ASSISTANT_VISIBLE; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN: + return AuthenticateReason.Face.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED: + return AuthenticateReason.Face.NOTIFICATION_PANEL_CLICKED; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED: + return AuthenticateReason.Face.OCCLUDING_APP_REQUESTED; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED: + return AuthenticateReason.Face.PICK_UP_GESTURE_TRIGGERED; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_QS_EXPANDED: + return AuthenticateReason.Face.QS_EXPANDED; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER: + return AuthenticateReason.Face.SWIPE_UP_ON_BOUNCER; + case FaceAuthenticateOptions.AUTHENTICATE_REASON_UDFPS_POINTER_DOWN: + return AuthenticateReason.Face.UDFPS_POINTER_DOWN; + default: + return AuthenticateReason.Face.UNKNOWN; + } + } + + @WakeReason + private int getWakeReason(@NonNull FaceAuthenticateOptions options) { + switch (options.getWakeReason()) { + case PowerManager.WAKE_REASON_POWER_BUTTON: + return WakeReason.POWER_BUTTON; + case PowerManager.WAKE_REASON_GESTURE: + return WakeReason.GESTURE; + case PowerManager.WAKE_REASON_WAKE_KEY: + return WakeReason.WAKE_KEY; + case PowerManager.WAKE_REASON_WAKE_MOTION: + return WakeReason.WAKE_MOTION; + case PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED: + return WakeReason.DISPLAY_GROUP_ADDED; + case PowerManager.WAKE_REASON_TAP: + return WakeReason.TAP; + case PowerManager.WAKE_REASON_LIFT: + return WakeReason.LIFT; + case PowerManager.WAKE_REASON_BIOMETRIC: + return WakeReason.BIOMETRIC; + case PowerManager.WAKE_REASON_CAMERA_LAUNCH: + case PowerManager.WAKE_REASON_HDMI: + case PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON: + case PowerManager.WAKE_REASON_UNFOLD_DEVICE: + case PowerManager.WAKE_REASON_DREAM_FINISHED: + case PowerManager.WAKE_REASON_TILT: + case PowerManager.WAKE_REASON_APPLICATION: + case PowerManager.WAKE_REASON_PLUGGED_IN: + default: + return WakeReason.UNKNOWN; + } + } + + @AuthenticateReason.Fingerprint + private int getAuthReason(@NonNull FingerprintAuthenticateOptions options) { + return AuthenticateReason.Fingerprint.UNKNOWN; + } + + @WakeReason + private int getWakeReason(@NonNull FingerprintAuthenticateOptions options) { + return WakeReason.UNKNOWN; + } + /** {@link OperationContext#id}. */ public int getId() { return mAidlContext.id; diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 005ad20a2d48..7b9fc36e8d61 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -74,6 +74,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> @Nullable private final TaskStackListener mTaskStackListener; private final LockoutTracker mLockoutTracker; + private final O mOptions; private final boolean mIsRestricted; private final boolean mAllowBackgroundAuthentication; // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update @@ -110,6 +111,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> mAllowBackgroundAuthentication = allowBackgroundAuthentication; mShouldUseLockoutTracker = lockoutTracker != null; mSensorStrength = sensorStrength; + mOptions = options; } @LockoutTracker.LockoutMode @@ -151,6 +153,11 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> return Utils.isSettings(getContext(), getOwnerString()); } + /** The options requested at the start of the operation. */ + protected O getOptions() { + return mOptions; + } + @Override protected boolean isCryptoOperation() { return mOperationId != 0; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 976f1cbe1e5c..84e2fb4a5966 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -166,7 +166,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut if (session.hasContextMethods()) { return session.getSession().authenticateWithContext( - mOperationId, getOperationContext().toAidlContext()); + mOperationId, getOperationContext().toAidlContext(getOptions())); } else { return session.getSession().authenticate(mOperationId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index e65202dca5cd..fa23ccd482fb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -47,6 +47,7 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements private static final String TAG = "FaceDetectClient"; private final boolean mIsStrongBiometric; + private final FaceAuthenticateOptions mOptions; @Nullable private ICancellationSignal mCancellationSignal; @Nullable private SensorPrivacyManager mSensorPrivacyManager; @@ -74,6 +75,7 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; mSensorPrivacyManager = sensorPrivacyManager; + mOptions = options; } @Override @@ -118,7 +120,7 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements if (session.hasContextMethods()) { return session.getSession().detectInteractionWithContext( - getOperationContext().toAidlContext()); + getOperationContext().toAidlContext(mOptions)); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 0f81f9f2660e..435e81d688bd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -285,7 +285,7 @@ class FingerprintAuthenticationClient if (session.hasContextMethods()) { return session.getSession().authenticateWithContext( - mOperationId, opContext.toAidlContext()); + mOperationId, opContext.toAidlContext(getOptions())); } else { return session.getSession().authenticate(mOperationId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 376d23187fb8..16d16fc95c5b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -48,6 +48,7 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements private static final String TAG = "FingerprintDetectClient"; private final boolean mIsStrongBiometric; + private final FingerprintAuthenticateOptions mOptions; @NonNull private final SensorOverlays mSensorOverlays; @Nullable private ICancellationSignal mCancellationSignal; @@ -66,6 +67,7 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements mIsStrongBiometric = isStrongBiometric; mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/, udfpsOverlay); + mOptions = options; } @Override @@ -105,7 +107,7 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements if (session.hasContextMethods()) { return session.getSession().detectInteractionWithContext( - getOperationContext().toAidlContext()); + getOperationContext().toAidlContext(mOptions)); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 4f28432a20a2..cc41207eaee1 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -3101,6 +3101,16 @@ public class LockSettingsService extends ILockSettings.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return; + + final long identity = Binder.clearCallingIdentity(); + try { + dumpInternal(printWriter); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void dumpInternal(PrintWriter printWriter) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println("Current lock settings service state:"); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 9329f063aee5..0d417e457509 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -136,9 +136,7 @@ public class Installer extends SystemService { } /** - * @param isolated indicates if this object should <em>not</em> connect to - * the real {@code installd}. All remote calls will be ignored - * unless you extend this class and intercept them. + * @param isolated Make the installer isolated. See {@link isIsolated}. */ public Installer(Context context, boolean isolated) { super(context); @@ -153,6 +151,15 @@ public class Installer extends SystemService { mWarnIfHeld = warnIfHeld; } + /** + * Returns true if the installer is isolated, i.e. if this object should <em>not</em> connect to + * the real {@code installd}. All remote calls will be ignored unless you extend this class and + * intercept them. + */ + public boolean isIsolated() { + return mIsolated; + } + @Override public void onStart() { if (mIsolated) { diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 767c0a73bc54..6a2ddc8f94b0 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static com.android.server.pm.DexOptHelper.useArtService; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; @@ -301,6 +302,15 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throws InstallerException { final StringBuilder builder = new StringBuilder(); + if (useArtService()) { + if ((dexFlags & DEXOPT_SECONDARY_DEX) != 0) { + // installd may change the reference profile in place for secondary dex + // files, which isn't safe with the lock free approach in ART Service. + throw new IllegalArgumentException( + "Invalid OTA dexopt call for secondary dex"); + } + } + // The current version. For v10, see b/115993344. builder.append("10 "); @@ -353,7 +363,6 @@ public class OtaDexoptService extends IOtaDexopt.Stub { PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( collectingInstaller, mPackageManagerService.mInstallLock, mContext); - // TODO(b/251903639): Allow this use of legacy dexopt code even when ART Service is enabled. try { optimizer.performDexOpt(pkg, pkgSetting, null /* ISAs */, null /* CompilerStats.PackageStats */, @@ -362,9 +371,19 @@ public class OtaDexoptService extends IOtaDexopt.Stub { new DexoptOptions(pkg.getPackageName(), compilationReason, DexoptOptions.DEXOPT_BOOT_COMPLETE)); } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); + // OTA is still allowed to use the legacy dexopt code even when ART Service is enabled. + // The installer is isolated and won't call into installd, and the dexopt() method is + // overridden to only collect the command above. Hence we shouldn't go into any code + // path where this exception is thrown. + Slog.wtf(TAG, e); } + // ART Service compat note: These commands are consumed by the otapreopt binary, which uses + // the same legacy dexopt code as installd to invoke dex2oat. It provides output path + // implementations (see calculate_odex_file_path and create_cache_path in + // frameworks/native/cmds/installd/otapreopt.cpp) to write to different odex files than + // those used by ART Service in its ordinary operations, so it doesn't interfere with ART + // Service even when dalvik.vm.useartservice is true. return commands; } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 0a90e7a30db6..8a4080ff029d 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; +import static com.android.server.pm.DexOptHelper.useArtService; import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE; import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE; import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS; @@ -329,8 +330,22 @@ public class PackageDexOptimizer { String profileName = ArtManager.getProfileName( i == 0 ? null : pkg.getSplitNames()[i - 1]); - final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() - || packageUseInfo.isUsedByOtherApps(path); + + final boolean isUsedByOtherApps; + if (options.isDexoptAsSharedLibrary()) { + isUsedByOtherApps = true; + } else if (useArtService()) { + // We get here when collecting dexopt commands in OTA preopt, even when ART Service + // is in use. packageUseInfo isn't useful in that case since the legacy dex use + // database hasn't been updated. So we'd have to query ART Service instead, but it + // doesn't provide that API. Just cop-out and bypass the cloud profile handling. + // That means such apps will get preopted wrong, and we'll leave it to a later + // background dexopt after reboot instead. + isUsedByOtherApps = false; + } else { + isUsedByOtherApps = packageUseInfo.isUsedByOtherApps(path); + } + String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter()); // If the app is used by other apps, we must not use the existing profile because it // may contain user data, unless the profile is newly created on install. @@ -446,6 +461,14 @@ public class PackageDexOptimizer { private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path, @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException { if (dexMetadataPath != null) { + if (mInstaller.isIsolated()) { + // If the installer is isolated, the two calls to it below will return immediately, + // so this only short-circuits that a bit. We need to do it to avoid the + // LegacyDexoptDisabledException getting thrown first, when we get here during OTA + // preopt and ART Service is enabled. + return true; + } + try { // Make sure we don't keep any existing contents. mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName); @@ -879,7 +902,12 @@ public class PackageDexOptimizer { private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade, int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); + // Allow calls from OtaDexoptService even when ART Service is in use. The installer is + // isolated in that case so later calls to it won't call into installd anyway. + if (!mInstaller.isIsolated()) { + Installer.checkLegacyDexoptDisabled(); + } + final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0; final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0; boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE; @@ -948,6 +976,8 @@ public class PackageDexOptimizer { */ private int analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter) throws LegacyDexoptDisabledException { + Installer.checkLegacyDexoptDisabled(); + // Check if we are allowed to merge and if the compiler filter is profile guided. if (!isProfileGuidedCompilerFilter(compilerFilter)) { return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 928ffa718c6f..3f9a0bc89641 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -24,6 +24,7 @@ import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; @@ -94,6 +95,7 @@ import com.android.server.EventLogTags; import com.android.server.IntentResolver; import com.android.server.LocalManagerRegistry; import com.android.server.Watchdog; +import com.android.server.am.ActivityManagerUtils; import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.pkg.AndroidPackage; @@ -1186,12 +1188,6 @@ public class PackageManagerServiceUtils { continue; } - // Only enforce filter matching if target app's target SDK >= T - if (!compat.isChangeEnabledInternal( - ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) { - continue; - } - final ParsedMainComponent comp; if (info instanceof ActivityInfo) { if (isReceiver) { @@ -1210,6 +1206,10 @@ public class PackageManagerServiceUtils { continue; } + // Only enforce filter matching if target app's target SDK >= T + final boolean enforce = compat.isChangeEnabledInternal( + ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo); + boolean match = false; for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); @@ -1219,14 +1219,19 @@ public class PackageManagerServiceUtils { } } if (!match) { - Slog.w(TAG, "Intent does not match component's intent filter: " + intent); - Slog.w(TAG, "Access blocked: " + comp.getComponentName()); - if (DEBUG_INTENT_MATCHING) { - Slog.v(TAG, "Component intent filters:"); - comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); - Slog.v(TAG, "-----------------------------"); + ActivityManagerUtils.logUnsafeIntentEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, + filterCallingUid, intent, resolvedType, enforce); + if (enforce) { + Slog.w(TAG, "Intent does not match component's intent filter: " + intent); + Slog.w(TAG, "Access blocked: " + comp.getComponentName()); + if (DEBUG_INTENT_MATCHING) { + Slog.v(TAG, "Component intent filters:"); + comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); + Slog.v(TAG, "-----------------------------"); + } + resolveInfos.remove(i); } - resolveInfos.remove(i); } } } diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index a13c568f87a6..7ed10a4df1db 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT; import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; import static com.android.server.pm.PackageManagerService.TAG; @@ -55,9 +56,9 @@ import android.util.Slog; import com.android.internal.app.ResolverActivity; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; +import com.android.server.am.ActivityManagerUtils; import com.android.server.compat.PlatformCompat; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -130,18 +131,9 @@ final class ResolveIntentHelper { boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid( ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, filterCallingUid); - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, - FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, - filterCallingUid, - query.get(i).getComponentInfo().getComponentName().flattenToShortString(), - callerPackage, - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - hasToBeExportedToMatch); + ActivityManagerUtils.logUnsafeIntentEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, + filterCallingUid, intent, resolvedType, hasToBeExportedToMatch); if (callback != null) { handler.post(() -> { try { diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index f11c864edba0..bc23020e8dbe 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -12688,8 +12688,8 @@ public class BatteryStatsImpl extends BatteryStats { energy = info.getControllerEnergyUsed(); if (!info.getUidTraffic().isEmpty()) { for (UidTraffic traffic : info.getUidTraffic()) { - uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes()); - uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes()); + uidRxBytes.put(traffic.getUid(), traffic.getRxBytes()); + uidTxBytes.put(traffic.getUid(), traffic.getTxBytes()); } } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 97b78116bbfa..370d21fe219e 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -137,7 +137,8 @@ public final class ProviderCreateSession extends ProviderSession< remoteCredentialService); mCompleteRequest = completeCreateRequest; setStatus(Status.PENDING); - mProviderResponseDataHandler = new ProviderResponseDataHandler(hybridService); + mProviderResponseDataHandler = new ProviderResponseDataHandler( + ComponentName.unflattenFromString(hybridService)); } @Override @@ -297,21 +298,23 @@ public final class ProviderCreateSession extends ProviderSession< } private class ProviderResponseDataHandler { - private final ComponentName mExpectedRemoteEntryProviderService; + @Nullable private final ComponentName mExpectedRemoteEntryProviderService; @NonNull private final Map<String, Pair<CreateEntry, Entry>> mUiCreateEntries = new HashMap<>(); @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null; - ProviderResponseDataHandler(String hybridService) { - mExpectedRemoteEntryProviderService = ComponentName.unflattenFromString(hybridService); + ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) { + mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService; } public void addResponseContent(List<CreateEntry> createEntries, RemoteEntry remoteEntry) { createEntries.forEach(this::addCreateEntry); - setRemoteEntry(remoteEntry); + if (remoteEntry != null) { + setRemoteEntry(remoteEntry); + } } public void addCreateEntry(CreateEntry createEntry) { String id = generateUniqueId(); @@ -321,13 +324,13 @@ public final class ProviderCreateSession extends ProviderSession< } public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) { - if (remoteEntry == null) { - mUiRemoteEntry = null; + if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) { + Log.i(TAG, "Remote entry being dropped as it does not meet the restriction" + + "checks."); return; } - if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) { - Log.i(TAG, "Remote entry being dropped as it is not from the service " - + "configured by the OEM."); + if (remoteEntry == null) { + mUiRemoteEntry = null; return; } String id = generateUniqueId(); diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index ee813e90ddfe..80a61b917acc 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -181,7 +181,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Called when the provider response has been updated by an external source. */ @Override // Callback from the remote provider public void onProviderResponseSuccess(@Nullable BeginGetCredentialResponse response) { - Log.i(TAG, "in onProviderResponseSuccess"); onSetInitialRemoteResponse(response); } @@ -393,7 +392,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential .extractResponseContent(providerPendingIntentResponse .getResultData()); if (response != null && !mProviderResponseDataHandler.isEmptyResponse(response)) { - addToInitialRemoteResponse(response); + addToInitialRemoteResponse(response, /*isInitialResponse=*/ false); // Additional content received is in the form of new response content. return true; } @@ -401,7 +400,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential return false; } - private void addToInitialRemoteResponse(BeginGetCredentialResponse content) { + private void addToInitialRemoteResponse(BeginGetCredentialResponse content, + boolean isInitialResponse) { if (content == null) { return; } @@ -409,7 +409,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential content.getCredentialEntries(), content.getActions(), content.getAuthenticationActions(), - content.getRemoteCredentialEntry() + content.getRemoteCredentialEntry(), + isInitialResponse ); } @@ -424,7 +425,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Updates the response being maintained in state by this provider session. */ private void onSetInitialRemoteResponse(BeginGetCredentialResponse response) { mProviderResponse = response; - addToInitialRemoteResponse(response); + addToInitialRemoteResponse(response, /*isInitialResponse=*/true); if (mProviderResponseDataHandler.isEmptyResponse(response)) { updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE); return; @@ -463,7 +464,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential } private class ProviderResponseDataHandler { - private final ComponentName mExpectedRemoteEntryProviderService; + @Nullable private final ComponentName mExpectedRemoteEntryProviderService; @NonNull private final Map<String, Pair<CredentialEntry, Entry>> mUiCredentialEntries = new HashMap<>(); @@ -475,19 +476,27 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null; - ProviderResponseDataHandler(ComponentName expectedRemoteEntryProviderService) { + ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) { mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService; } public void addResponseContent(List<CredentialEntry> credentialEntries, List<Action> actions, List<Action> authenticationActions, - RemoteEntry remoteEntry) { + RemoteEntry remoteEntry, boolean isInitialResponse) { credentialEntries.forEach(this::addCredentialEntry); actions.forEach(this::addAction); authenticationActions.forEach( authenticationAction -> addAuthenticationAction(authenticationAction, AuthenticationEntry.STATUS_LOCKED)); - setRemoteEntry(remoteEntry); + // In the query phase, it is likely most providers will return a null remote entry + // so no need to invoke the setter since it adds the overhead of checking for the + // hybrid permission, and then sets an already null value to null. + // If this is not the query phase, e.g. response after a locked entry is unlocked + // then it is valid for the provider to remove the remote entry, and so we allow + // them to set it to null. + if (remoteEntry != null || !isInitialResponse) { + setRemoteEntry(remoteEntry); + } } public void addCredentialEntry(CredentialEntry credentialEntry) { String id = generateUniqueId(); @@ -524,12 +533,13 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential } public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) { - if (remoteEntry == null) { + if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) { + Log.i(TAG, "Remote entry being dropped as it does not meet the restriction" + + " checks."); return; } - if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) { - Log.i(TAG, "Remote entry being dropped as it is not from the service " - + "configured by the OEM."); + if (remoteEntry == null) { + mUiRemoteEntry = null; return; } String id = generateUniqueId(); @@ -538,6 +548,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential mUiRemoteEntry = new Pair<>(generateUniqueId(), new Pair<>(remoteEntry, entry)); } + + public GetCredentialProviderData toGetCredentialProviderData() { return new GetCredentialProviderData.Builder( mComponentName.flattenToString()).setActionChips(prepareActionEntries()) @@ -571,7 +583,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential return credEntries; } - private Entry prepareRemoteEntry() { if (mUiRemoteEntry == null || mUiRemoteEntry.first == null || mUiRemoteEntry.second == null) { diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index ecddcf30f88d..53ed070e3e49 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -19,10 +19,13 @@ package com.android.server.credentials; import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_FAILURE; import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_SUCCESS; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.credentials.Credential; import android.credentials.ui.ProviderData; import android.credentials.ui.ProviderPendingIntentResponse; @@ -228,6 +231,39 @@ public abstract class ProviderSession<T, R> return mProviderResponse; } + protected boolean enforceRemoteEntryRestrictions( + @Nullable ComponentName expectedRemoteEntryProviderService) { + // Check if the service is the one set by the OEM. If not silently reject this entry + if (!mComponentName.equals(expectedRemoteEntryProviderService)) { + Log.i(TAG, "Remote entry being dropped as it is not from the service " + + "configured by the OEM."); + return false; + } + // Check if the service has the hybrid permission .If not, silently reject this entry. + // This check is in addition to the permission check happening in the provider's process. + try { + ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( + mComponentName.getPackageName(), + PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY)); + if (appInfo != null + && mContext.checkPermission( + Manifest.permission.PROVIDE_REMOTE_CREDENTIALS, + /*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) { + return true; + } + } catch (SecurityException e) { + Log.i(TAG, "Error getting info for " + + mComponentName.flattenToString() + ": " + e.getMessage()); + return false; + } catch (PackageManager.NameNotFoundException e) { + Log.i(TAG, "Error getting info for " + + mComponentName.flattenToString() + ": " + e.getMessage()); + return false; + } + Log.i(TAG, "In enforceRemoteEntryRestrictions - remote entry checks fail"); + return false; + } + /** Should be overridden to prepare, and stores state for {@link ProviderData} to be * shown on the UI. */ @Nullable protected abstract ProviderData prepareUiData(); diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java index 702261ea43f5..ff4e3b680131 100644 --- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java +++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java @@ -121,8 +121,6 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr ProviderCallbacks<BeginGetCredentialResponse> callback) { Log.i(TAG, "In onGetCredentials in RemoteCredentialService"); AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); - AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef = - new AtomicReference<>(); CompletableFuture<BeginGetCredentialResponse> connectThenExecute = postAsync(service -> { CompletableFuture<BeginGetCredentialResponse> getCredentials = @@ -134,7 +132,6 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr new IBeginGetCredentialCallback.Stub() { @Override public void onSuccess(BeginGetCredentialResponse response) { - Log.i(TAG, "In onSuccess in RemoteCredentialService"); getCredentials.complete(response); } @@ -147,22 +144,15 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr new GetCredentialException(errorType, errorMsg)); } }); - CompletableFuture<BeginGetCredentialResponse> future = futureRef.get(); - if (future != null && future.isCancelled()) { - dispatchCancellationSignal(cancellationSignal); - } else { - cancellationSink.set(cancellationSignal); - } + cancellationSink.set(cancellationSignal); return getCredentials; } finally { Binder.restoreCallingIdentity(originalCallingUidToken); } }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); - futureRef.set(connectThenExecute); connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> handleExecutionResponse(result, error, cancellationSink, callback))); - return cancellationSink.get(); } @@ -178,8 +168,6 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr ProviderCallbacks<BeginCreateCredentialResponse> callback) { Log.i(TAG, "In onCreateCredential in RemoteCredentialService"); AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); - AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef = - new AtomicReference<>(); CompletableFuture<BeginCreateCredentialResponse> connectThenExecute = postAsync(service -> { @@ -205,19 +193,13 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr new CreateCredentialException(errorType, errorMsg)); } }); - CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get(); - if (future != null && future.isCancelled()) { - dispatchCancellationSignal(cancellationSignal); - } else { - cancellationSink.set(cancellationSignal); - } + cancellationSink.set(cancellationSignal); return createCredentialFuture; } finally { Binder.restoreCallingIdentity(originalCallingUidToken); } }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); - futureRef.set(connectThenExecute); connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> handleExecutionResponse(result, error, cancellationSink, callback))); @@ -236,7 +218,6 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr ProviderCallbacks<Void> callback) { Log.i(TAG, "In onClearCredentialState in RemoteCredentialService"); AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); - AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>(); CompletableFuture<Void> connectThenExecute = postAsync(service -> { @@ -263,19 +244,13 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr errorMsg)); } }); - CompletableFuture<Void> future = futureRef.get(); - if (future != null && future.isCancelled()) { - dispatchCancellationSignal(cancellationSignal); - } else { - cancellationSink.set(cancellationSignal); - } + cancellationSink.set(cancellationSignal); return clearCredentialFuture; } finally { Binder.restoreCallingIdentity(originalCallingUidToken); } }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); - futureRef.set(connectThenExecute); connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> handleExecutionResponse(result, error, cancellationSink, callback))); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index 3ff802c0125c..6b0e33037af8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.face.aidl; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -30,11 +32,15 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.ComponentName; +import android.hardware.biometrics.common.AuthenticateReason; import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.WakeReason; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; import android.hardware.face.FaceAuthenticateOptions; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; @@ -69,6 +75,8 @@ public class FaceAuthenticationClientTest { private static final int USER_ID = 12; private static final long OP_ID = 32; + private static final int WAKE_REASON = WakeReason.LIFT; + private static final int AUTH_REASON = AuthenticateReason.Face.ASSISTANT_VISIBLE; @Rule public final TestableContext mContext = new TestableContext( @@ -126,8 +134,13 @@ public class FaceAuthenticationClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).authenticateWithContext( - eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext())); + + final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext(); + order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext)); + assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON); + assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason()) + .isEqualTo(AUTH_REASON); + verify(mHal, never()).authenticate(anyLong()); } @@ -156,8 +169,11 @@ public class FaceAuthenticationClientTest { final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder() .setOpPackageName("test-owner") - .setUserId(5) + .setUserId(USER_ID) .setSensorId(9) + .setWakeReason(PowerManager.WAKE_REASON_LIFT) + .setAuthenticateReason( + FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE) .build(); return new FaceAuthenticationClient(mContext, () -> aidl, mToken, 2 /* requestId */, mClientMonitorCallbackConverter, OP_ID, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java index c4c550549f73..0abfa7e6546d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.face.aidl; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.inOrder; @@ -24,9 +26,13 @@ import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.hardware.biometrics.common.AuthenticateReason; +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.WakeReason; import android.hardware.biometrics.face.ISession; import android.hardware.face.FaceAuthenticateOptions; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; @@ -55,6 +61,8 @@ import org.mockito.junit.MockitoRule; public class FaceDetectClientTest { private static final int USER_ID = 12; + private static final int WAKE_REASON = WakeReason.POWER_BUTTON; + private static final int AUTH_REASON = AuthenticateReason.Face.OCCLUDING_APP_REQUESTED; @Rule public final TestableContext mContext = new TestableContext( @@ -103,8 +111,13 @@ public class FaceDetectClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).detectInteractionWithContext( - same(mOperationContextCaptor.getValue().toAidlContext())); + + final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext(); + order.verify(mHal).detectInteractionWithContext(same(aidlContext)); + assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON); + assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason()) + .isEqualTo(AUTH_REASON); + verify(mHal, never()).detectInteraction(); } @@ -118,6 +131,9 @@ public class FaceDetectClientTest { .setUserId(USER_ID) .setSensorId(5) .setOpPackageName("own-it") + .setWakeReason(PowerManager.WAKE_REASON_POWER_BUTTON) + .setAuthenticateReason( + FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED) .build(), mBiometricLogger, mBiometricContext, false /* isStrongBiometric */, null /* sensorPrivacyManager */); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index f0f975ccf5ff..c6645003c7e2 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -36,6 +36,7 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.ComponentName; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.common.AuthenticateReason; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.ISession; @@ -165,8 +166,12 @@ public class FingerprintAuthenticationClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).authenticateWithContext( - eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext())); + + final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext(); + order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext)); + assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason()) + .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN); + verify(mHal, never()).authenticate(anyLong()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java index e741e446da85..c20cc392f5c0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.inOrder; @@ -24,6 +26,8 @@ import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.hardware.biometrics.common.AuthenticateReason; +import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -108,8 +112,12 @@ public class FingerprintDetectClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).detectInteractionWithContext( - same(mOperationContextCaptor.getValue().toAidlContext())); + + final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext(); + order.verify(mHal).detectInteractionWithContext(same(aidlContext)); + assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason()) + .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN); + verify(mHal, never()).detectInteraction(); } diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java index c6a7fbcfb454..ee4b839dda5a 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java @@ -572,41 +572,14 @@ public class BatteryStatsImplTest { mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000); mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000); - - - final Parcel uidTrafficParcel1 = Parcel.obtain(); - final Parcel uidTrafficParcel2 = Parcel.obtain(); - - uidTrafficParcel1.writeInt(10042); - uidTrafficParcel1.writeLong(3000); - uidTrafficParcel1.writeLong(4000); - uidTrafficParcel1.setDataPosition(0); - uidTrafficParcel2.writeInt(10043); - uidTrafficParcel2.writeLong(5000); - uidTrafficParcel2.writeLong(8000); - uidTrafficParcel2.setDataPosition(0); - - List<UidTraffic> uidTrafficList = ImmutableList.of( - UidTraffic.CREATOR.createFromParcel(uidTrafficParcel1), - UidTraffic.CREATOR.createFromParcel(uidTrafficParcel2)); - - final Parcel btActivityEnergyInfoParcel = Parcel.obtain(); - btActivityEnergyInfoParcel.writeLong(1000); - btActivityEnergyInfoParcel.writeInt( - BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE); - btActivityEnergyInfoParcel.writeLong(9000); - btActivityEnergyInfoParcel.writeLong(8000); - btActivityEnergyInfoParcel.writeLong(12000); - btActivityEnergyInfoParcel.writeLong(0); - btActivityEnergyInfoParcel.writeTypedList(uidTrafficList); - btActivityEnergyInfoParcel.setDataPosition(0); - - BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR - .createFromParcel(btActivityEnergyInfoParcel); - - uidTrafficParcel1.recycle(); - uidTrafficParcel2.recycle(); - btActivityEnergyInfoParcel.recycle(); + BluetoothActivityEnergyInfo info = createBluetoothActivityEnergyInfo( + /* timestamp= */ 1000, + /* controllerTxTimeMs= */ 9000, + /* controllerRxTimeMs= */ 8000, + /* controllerIdleTimeMs= */ 12000, + /* controllerEnergyUsed= */ 0, + createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000), + createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000)); mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000); @@ -622,4 +595,105 @@ public class BatteryStatsImplTest { assertThat(uidStats.rxTimeMs).isEqualTo(7375); // Some scan time is treated as RX assertThat(uidStats.txTimeMs).isEqualTo(7666); // Some scan time is treated as TX } + + /** A regression test for b/266128651 */ + @Test + public void testGetNetworkActivityBytes_multipleUpdates() { + when(mPowerProfile.getAveragePower( + PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0); + mBatteryStatsImpl.setOnBatteryInternal(true); + mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + + BluetoothActivityEnergyInfo info1 = createBluetoothActivityEnergyInfo( + /* timestamp= */ 10000, + /* controllerTxTimeMs= */ 9000, + /* controllerRxTimeMs= */ 8000, + /* controllerIdleTimeMs= */ 2000, + /* controllerEnergyUsed= */ 0, + createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000), + createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000)); + + mBatteryStatsImpl.updateBluetoothStateLocked(info1, -1, 1000, 1000); + + long totalRx1 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED); + long totalTx1 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED); + + assertThat(totalRx1).isEqualTo(8000); // 3000 + 5000 + assertThat(totalTx1).isEqualTo(12000); // 4000 + 8000 + + BluetoothActivityEnergyInfo info2 = createBluetoothActivityEnergyInfo( + /* timestamp= */ 20000, + /* controllerTxTimeMs= */ 19000, + /* controllerRxTimeMs= */ 18000, + /* controllerIdleTimeMs= */ 3000, + /* controllerEnergyUsed= */ 0, + createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 6000, /* txBytes= */ 9500), + createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 7000, /* txBytes= */ 9000)); + + mBatteryStatsImpl.updateBluetoothStateLocked(info2, -1, 2000, 2000); + + long totalRx2 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED); + long totalTx2 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED); + + assertThat(totalRx2).isEqualTo(16000); // 3000 + 6000 (updated) + 7000 (new) + assertThat(totalTx2).isEqualTo(22500); // 4000 + 9500 (updated) + 9000 (new) + + BluetoothActivityEnergyInfo info3 = createBluetoothActivityEnergyInfo( + /* timestamp= */ 30000, + /* controllerTxTimeMs= */ 20000, + /* controllerRxTimeMs= */ 20000, + /* controllerIdleTimeMs= */ 4000, + /* controllerEnergyUsed= */ 0, + createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 7000, /* txBytes= */ 9900), + createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 8000, /* txBytes= */ 10000)); + + mBatteryStatsImpl.updateBluetoothStateLocked(info3, -1, 2000, 2000); + + long totalRx3 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED); + long totalTx3 = mBatteryStatsImpl.getNetworkActivityBytes( + BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED); + + assertThat(totalRx3).isEqualTo(18000); // 3000 + 7000 (updated) + 8000 (updated) + assertThat(totalTx3).isEqualTo(23900); // 4000 + 9900 (updated) + 10000 (updated) + } + + private UidTraffic createUidTraffic(int appUid, long rxBytes, long txBytes) { + final Parcel parcel = Parcel.obtain(); + parcel.writeInt(appUid); // mAppUid + parcel.writeLong(rxBytes); // mRxBytes + parcel.writeLong(txBytes); // mTxBytes + parcel.setDataPosition(0); + UidTraffic uidTraffic = UidTraffic.CREATOR.createFromParcel(parcel); + parcel.recycle(); + return uidTraffic; + } + + private BluetoothActivityEnergyInfo createBluetoothActivityEnergyInfo( + long timestamp, + long controllerTxTimeMs, + long controllerRxTimeMs, + long controllerIdleTimeMs, + long controllerEnergyUsed, + UidTraffic... uidTraffic) { + Parcel parcel = Parcel.obtain(); + parcel.writeLong(timestamp); // mTimestamp + parcel.writeInt( + BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE); // mBluetoothStackState + parcel.writeLong(controllerTxTimeMs); // mControllerTxTimeMs; + parcel.writeLong(controllerRxTimeMs); // mControllerRxTimeMs; + parcel.writeLong(controllerIdleTimeMs); // mControllerIdleTimeMs; + parcel.writeLong(controllerEnergyUsed); // mControllerEnergyUsed; + parcel.writeTypedList(ImmutableList.copyOf(uidTraffic)); // mUidTraffic + parcel.setDataPosition(0); + + BluetoothActivityEnergyInfo info = + BluetoothActivityEnergyInfo.CREATOR.createFromParcel(parcel); + parcel.recycle(); + return info; + } } diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java index 2cdb94526839..7deb8c73d1fc 100644 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java @@ -86,7 +86,7 @@ public class ProtoLogImplTest { mFile = testContext.getFileStreamPath("tracing_test.dat"); //noinspection ResultOfMethodCallIgnored mFile.delete(); - mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader); + mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader, 1024); } @After diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp index e53ba3e18a86..a1df878df12e 100644 --- a/tools/codegen/Android.bp +++ b/tools/codegen/Android.bp @@ -9,7 +9,7 @@ package { java_binary_host { name: "codegen_cli", - manifest: "manifest.txt", + main_class: "com.android.codegen.MainKt", srcs: [ "src/**/*.kt", ], diff --git a/tools/codegen/BUILD.bazel b/tools/codegen/BUILD.bazel deleted file mode 100644 index c14046d674dc..000000000000 --- a/tools/codegen/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -# TODO(b/245731902): auto-generate these with bp2build. -load("@rules_kotlin//kotlin:jvm_library.bzl", "kt_jvm_library") - -java_binary( - name = "codegen_cli", - main_class = "com.android.codegen.MainKt", - runtime_deps = [ - ":codegen_cli_kt_lib", - ], -) - -kt_jvm_library( - name = "codegen_cli_kt_lib", - srcs = glob(["src/**/*.kt"]), - deps = ["//external/javaparser"], -) - -kt_jvm_library( - name = "codegen-version-info", - srcs = glob(["src/**/SharedConstants.kt"]), -) diff --git a/tools/codegen/manifest.txt b/tools/codegen/manifest.txt deleted file mode 100644 index 6e1018ba6b55..000000000000 --- a/tools/codegen/manifest.txt +++ /dev/null @@ -1 +0,0 @@ -Main-class: com.android.codegen.MainKt |