diff options
111 files changed, 1763 insertions, 1540 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp new file mode 100644 index 000000000000..16907b3432ec --- /dev/null +++ b/AconfigFlags.bp @@ -0,0 +1,43 @@ +// 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. + +// Aconfig declarations and libraries for the core framework +java_defaults { + name: "framework-minus-apex-aconfig-libraries", + + // Add java_aconfig_libraries to here to add them to the core framework + srcs: [ + ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}", + ], +} + +// Default flags for java_aconfig_libraries that go into framework-minus-apex +// These libraries will not work standalone +java_defaults { + name: "framework-minus-apex-aconfig-java-defaults", + sdk_version: "core_platform", + libs: ["fake_device_config"], +} + +aconfig_declarations { + name: "com.android.hardware.camera2-aconfig", + package: "com.android.hardware.camera2", + srcs: ["core/java/android/hardware/camera2/camera_platform.aconfig"], +} + +java_aconfig_library { + name: "com.android.hardware.camera2-aconfig-java", + aconfig_declarations: "com.android.hardware.camera2-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index be589b2857b4..53554bce43b8 100644 --- a/Android.bp +++ b/Android.bp @@ -267,6 +267,7 @@ java_defaults { defaults: [ "framework-aidl-export-defaults", "latest_android_hardware_soundtrigger3_java_static", + "framework-minus-apex-aconfig-libraries", ], srcs: [ ":framework-non-updatable-sources", @@ -701,6 +702,7 @@ stubs_defaults { } build = [ + "AconfigFlags.bp", "ProtoLibraries.bp", "TestProtoLibraries.bp", ] @@ -16,8 +16,6 @@ narayan@google.com #{LAST_RESORT_SUGGESTION} ogunwale@google.com #{LAST_RESORT_SUGGESTION} roosa@google.com #{LAST_RESORT_SUGGESTION} smoreland@google.com #{LAST_RESORT_SUGGESTION} -svetoslavganov@android.com #{LAST_RESORT_SUGGESTION} -svetoslavganov@google.com #{LAST_RESORT_SUGGESTION} yamasani@google.com #{LAST_RESORT_SUGGESTION} # API changes are already covered by API-Review+1 (http://mdb/android-api-council) @@ -30,7 +28,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,**.mk = hansson@google.com, joeo@google.com per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS diff --git a/api/Android.bp b/api/Android.bp index c16bce5a1aea..e9cc40513221 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -83,6 +83,7 @@ combined_apis { "framework-configinfrastructure", "framework-connectivity", "framework-connectivity-t", + "framework-crashrecovery", "framework-devicelock", "framework-graphics", "framework-healthfitness", @@ -104,6 +105,7 @@ combined_apis { system_server_classpath: [ "service-art", "service-configinfrastructure", + "service-crashrecovery", "service-healthfitness", "service-media-s", "service-permission", diff --git a/boot/Android.bp b/boot/Android.bp index 83a46c522317..93d425e439a9 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -84,6 +84,10 @@ custom_platform_bootclasspath { module: "com.android.conscrypt-bootclasspath-fragment", }, { + apex: "com.android.crashrecovery", + module: "com.android.crashrecovery-bootclasspath-fragment", + }, + { apex: "com.android.devicelock", module: "com.android.devicelock-bootclasspath-fragment", }, diff --git a/core/api/current.txt b/core/api/current.txt index a3d8978caa21..3cb7468c1444 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -44883,6 +44883,7 @@ package android.telephony { field public static final int OUT_OF_NETWORK = 11; // 0xb field public static final int OUT_OF_SERVICE = 18; // 0x12 field public static final int POWER_OFF = 17; // 0x11 + field public static final int SATELLITE_ENABLED = 82; // 0x52 field public static final int SERVER_ERROR = 12; // 0xc field public static final int SERVER_UNREACHABLE = 9; // 0x9 field public static final int TIMED_OUT = 13; // 0xd @@ -46007,6 +46008,7 @@ package android.telephony { field public static final int ERI_FLASH = 2; // 0x2 field public static final int ERI_OFF = 1; // 0x1 field public static final int ERI_ON = 0; // 0x0 + field public static final String EVENT_DISPLAY_SOS_MESSAGE = "android.telephony.event.DISPLAY_SOS_MESSAGE"; field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT"; field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL"; field public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b1c3b4aef0f4..773d720d73fb 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -837,6 +837,23 @@ package android.appwidget { package android.companion { + public static final class AssociationInfo.Builder { + ctor public AssociationInfo.Builder(int, int, @NonNull String); + ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo); + method @NonNull public android.companion.AssociationInfo build(); + method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice); + method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress); + method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String); + method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence); + method @NonNull public android.companion.AssociationInfo.Builder setLastTimeConnected(long); + method @NonNull public android.companion.AssociationInfo.Builder setNotifyOnDeviceNearby(boolean); + method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean); + method @NonNull public android.companion.AssociationInfo.Builder setSelfManaged(boolean); + method @NonNull public android.companion.AssociationInfo.Builder setSystemDataSyncFlags(int); + method @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String); + method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long); + } + public final class CompanionDeviceManager { method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean); field public static final int MESSAGE_REQUEST_PING = 1669362552; // 0x63807378 diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java index 7d62c79e7519..083fa0041b26 100644 --- a/core/java/android/companion/AssociationInfo.java +++ b/core/java/android/companion/AssociationInfo.java @@ -17,7 +17,9 @@ package android.companion; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UserIdInt; import android.net.MacAddress; import android.os.Parcel; @@ -41,24 +43,26 @@ public final class AssociationInfo implements Parcelable { private static final String LAST_TIME_CONNECTED_NONE = "None"; /** * A unique ID of this Association record. - * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in + * Disclosed to the clients (i.e. companion applications) for referring to this record (e.g. in * {@code disassociate()} API call). */ private final int mId; - - private final @UserIdInt int mUserId; - private final @NonNull String mPackageName; - - private final @Nullable MacAddress mDeviceMacAddress; - private final @Nullable CharSequence mDisplayName; - private final @Nullable String mDeviceProfile; - private final @Nullable AssociatedDevice mAssociatedDevice; - + @UserIdInt + private final int mUserId; + @NonNull + private final String mPackageName; + @Nullable + private final String mTag; + @Nullable + private final MacAddress mDeviceMacAddress; + @Nullable + private final CharSequence mDisplayName; + @Nullable + private final String mDeviceProfile; + @Nullable + private final AssociatedDevice mAssociatedDevice; private final boolean mSelfManaged; private final boolean mNotifyOnDeviceNearby; - private final int mSystemDataSyncFlags; - private final String mTag; - /** * Indicates that the association has been revoked (removed), but we keep the association * record for final clean up (e.g. removing the app from the list of the role holders). @@ -72,6 +76,7 @@ public final class AssociationInfo implements Parcelable { * Default value is Long.MAX_VALUE. */ private final long mLastTimeConnectedMs; + private final int mSystemDataSyncFlags; /** * Creates a new Association. @@ -93,16 +98,13 @@ public final class AssociationInfo implements Parcelable { } mId = id; - mUserId = userId; mPackageName = packageName; - mDeviceMacAddress = macAddress; mDisplayName = displayName; mTag = tag; mDeviceProfile = deviceProfile; mAssociatedDevice = associatedDevice; - mSelfManaged = selfManaged; mNotifyOnDeviceNearby = notifyOnDeviceNearby; mRevoked = revoked; @@ -119,18 +121,11 @@ public final class AssociationInfo implements Parcelable { } /** - * @return the tag of this association. - * @see CompanionDeviceManager#setAssociationTag(int, String) - */ - public @Nullable String getTag() { - return mTag; - } - - /** * @return the ID of the user who "owns" this association. * @hide */ - public @UserIdInt int getUserId() { + @UserIdInt + public int getUserId() { return mUserId; } @@ -139,19 +134,31 @@ public final class AssociationInfo implements Parcelable { * @hide */ @SystemApi - public @NonNull String getPackageName() { + @NonNull + public String getPackageName() { return mPackageName; } /** + * @return the tag of this association. + * @see CompanionDeviceManager#setAssociationTag(int, String) + */ + @Nullable + public String getTag() { + return mTag; + } + + /** * @return the MAC address of the device. */ - public @Nullable MacAddress getDeviceMacAddress() { + @Nullable + public MacAddress getDeviceMacAddress() { return mDeviceMacAddress; } /** @hide */ - public @Nullable String getDeviceMacAddressAsString() { + @Nullable + public String getDeviceMacAddressAsString() { return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null; } @@ -161,7 +168,8 @@ public final class AssociationInfo implements Parcelable { * * @see AssociationRequest.Builder#setDisplayName(CharSequence) */ - public @Nullable CharSequence getDisplayName() { + @Nullable + public CharSequence getDisplayName() { return mDisplayName; } @@ -170,7 +178,8 @@ public final class AssociationInfo implements Parcelable { * association, or {@code null} if no specific profile was used. * @see AssociationRequest.Builder#setDeviceProfile(String) */ - public @Nullable String getDeviceProfile() { + @Nullable + public String getDeviceProfile() { return mDeviceProfile; } @@ -187,7 +196,8 @@ public final class AssociationInfo implements Parcelable { * @return the companion device that was associated, or {@code null} if the device is * self-managed or this association info was retrieved from persistent storage. */ - public @Nullable AssociatedDevice getAssociatedDevice() { + @Nullable + public AssociatedDevice getAssociatedDevice() { return mAssociatedDevice; } @@ -228,14 +238,14 @@ public final class AssociationInfo implements Parcelable { * @return the last time self reported disconnected for selfManaged only. * @hide */ - public Long getLastTimeConnectedMs() { + public long getLastTimeConnectedMs() { return mLastTimeConnectedMs; } /** * @return Enabled system data sync flags set via - * {@link CompanionDeviceManager#enableSystemDataSync(int, int)} and - * {@link CompanionDeviceManager#disableSystemDataSync(int, int)}. + * {@link CompanionDeviceManager#enableSystemDataSyncForTypes(int, int)} (int, int)} and + * {@link CompanionDeviceManager#disableSystemDataSyncForTypes(int, int)} (int, int)}. * Or by default all flags are 1 (enabled). */ public int getSystemDataSyncFlags() { @@ -279,7 +289,8 @@ public final class AssociationInfo implements Parcelable { } /** @hide */ - public @NonNull String toShortString() { + @NonNull + public String toShortString() { final StringBuilder sb = new StringBuilder(); sb.append("id=").append(mId); if (mDeviceMacAddress != null) { @@ -350,16 +361,13 @@ public final class AssociationInfo implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mId); - dest.writeInt(mUserId); dest.writeString(mPackageName); dest.writeString(mTag); - dest.writeTypedObject(mDeviceMacAddress, 0); dest.writeCharSequence(mDisplayName); dest.writeString(mDeviceProfile); dest.writeTypedObject(mAssociatedDevice, 0); - dest.writeBoolean(mSelfManaged); dest.writeBoolean(mNotifyOnDeviceNearby); dest.writeBoolean(mRevoked); @@ -370,16 +378,13 @@ public final class AssociationInfo implements Parcelable { private AssociationInfo(@NonNull Parcel in) { mId = in.readInt(); - mUserId = in.readInt(); mPackageName = in.readString(); mTag = in.readString(); - mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR); mDisplayName = in.readCharSequence(); mDeviceProfile = in.readString(); mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR); - mSelfManaged = in.readBoolean(); mNotifyOnDeviceNearby = in.readBoolean(); mRevoked = in.readBoolean(); @@ -403,139 +408,182 @@ public final class AssociationInfo implements Parcelable { }; /** - * Use this method to obtain a builder that you can use to create a copy of the - * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected} - * or {@code mNotifyOnDeviceNearby}. - * <p> - * Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long) - * setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean) - * setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}. - * - * This is ensured statically at compile time. + * Builder for {@link AssociationInfo} * * @hide */ - @NonNull - public static NonActionableBuilder builder(@NonNull AssociationInfo info) { - return new Builder(info); - } - - /** @hide */ - public static final class Builder implements NonActionableBuilder { - @NonNull - private final AssociationInfo mOriginalInfo; + @TestApi + public static final class Builder { + private final int mId; + private final int mUserId; + private final String mPackageName; + private String mTag; + private MacAddress mDeviceMacAddress; + private CharSequence mDisplayName; + private String mDeviceProfile; + private AssociatedDevice mAssociatedDevice; + private boolean mSelfManaged; private boolean mNotifyOnDeviceNearby; private boolean mRevoked; + private long mTimeApprovedMs; private long mLastTimeConnectedMs; private int mSystemDataSyncFlags; - private String mTag; - private Builder(@NonNull AssociationInfo info) { - mOriginalInfo = info; + /** @hide */ + @TestApi + public Builder(int id, int userId, @NonNull String packageName) { + mId = id; + mUserId = userId; + mPackageName = packageName; + } + + /** @hide */ + @TestApi + public Builder(@NonNull AssociationInfo info) { + mId = info.mId; + mUserId = info.mUserId; + mPackageName = info.mPackageName; mTag = info.mTag; + mDeviceMacAddress = info.mDeviceMacAddress; + mDisplayName = info.mDisplayName; + mDeviceProfile = info.mDeviceProfile; + mAssociatedDevice = info.mAssociatedDevice; + mSelfManaged = info.mSelfManaged; mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby; mRevoked = info.mRevoked; + mTimeApprovedMs = info.mTimeApprovedMs; mLastTimeConnectedMs = info.mLastTimeConnectedMs; mSystemDataSyncFlags = info.mSystemDataSyncFlags; } /** @hide */ - @Override + @TestApi @NonNull - public Builder setLastTimeConnected(long lastTimeConnectedMs) { - if (lastTimeConnectedMs < 0) { - throw new IllegalArgumentException( - "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs - + " )"); - } - mLastTimeConnectedMs = lastTimeConnectedMs; + public Builder setTag(@Nullable String tag) { + mTag = tag; return this; } /** @hide */ - @Override + @TestApi @NonNull - public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) { - mNotifyOnDeviceNearby = notifyOnDeviceNearby; + public Builder setDeviceMacAddress(@Nullable MacAddress deviceMacAddress) { + mDeviceMacAddress = deviceMacAddress; return this; } /** @hide */ - @Override + @TestApi @NonNull - public Builder setRevoked(boolean revoked) { - mRevoked = revoked; + public Builder setDisplayName(@Nullable CharSequence displayName) { + mDisplayName = displayName; return this; } /** @hide */ - @Override + @TestApi @NonNull - public Builder setSystemDataSyncFlags(int flags) { - mSystemDataSyncFlags = flags; + public Builder setDeviceProfile(@Nullable String deviceProfile) { + mDeviceProfile = deviceProfile; return this; } /** @hide */ - @Override + @TestApi @NonNull - public Builder setTag(String tag) { - mTag = tag; + public Builder setAssociatedDevice(@Nullable AssociatedDevice associatedDevice) { + mAssociatedDevice = associatedDevice; return this; } /** @hide */ + @TestApi @NonNull - public AssociationInfo build() { - return new AssociationInfo( - mOriginalInfo.mId, - mOriginalInfo.mUserId, - mOriginalInfo.mPackageName, - mTag, - mOriginalInfo.mDeviceMacAddress, - mOriginalInfo.mDisplayName, - mOriginalInfo.mDeviceProfile, - mOriginalInfo.mAssociatedDevice, - mOriginalInfo.mSelfManaged, - mNotifyOnDeviceNearby, - mRevoked, - mOriginalInfo.mTimeApprovedMs, - mLastTimeConnectedMs, - mSystemDataSyncFlags - ); + public Builder setSelfManaged(boolean selfManaged) { + mSelfManaged = selfManaged; + return this; } - } - /** - * This interface is returned from the - * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point - * to indicate that this builder is not yet in a state that can produce a meaningful - * {@link AssociationInfo} object that is different from the one originally passed in. - * - * <p> - * Only by calling one of the setter methods is this builder turned into one where calling - * {@link Builder#build() build()} makes sense. - * - * @hide - */ - public interface NonActionableBuilder { /** @hide */ + @TestApi @NonNull - Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby); + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) { + mNotifyOnDeviceNearby = notifyOnDeviceNearby; + return this; + } + + /** @hide */ + @TestApi + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setRevoked(boolean revoked) { + mRevoked = revoked; + return this; + } /** @hide */ + @TestApi @NonNull - Builder setLastTimeConnected(long lastTimeConnectedMs); + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setTimeApproved(long timeApprovedMs) { + if (timeApprovedMs < 0) { + throw new IllegalArgumentException("timeApprovedMs must be positive. Was given (" + + timeApprovedMs + ")"); + } + mTimeApprovedMs = timeApprovedMs; + return this; + } /** @hide */ + @TestApi @NonNull - Builder setRevoked(boolean revoked); + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setLastTimeConnected(long lastTimeConnectedMs) { + if (lastTimeConnectedMs < 0) { + throw new IllegalArgumentException( + "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs + + " )"); + } + mLastTimeConnectedMs = lastTimeConnectedMs; + return this; + } /** @hide */ + @TestApi @NonNull - Builder setSystemDataSyncFlags(int flags); + public Builder setSystemDataSyncFlags(int flags) { + mSystemDataSyncFlags = flags; + return this; + } /** @hide */ - Builder setTag(String tag); + @TestApi + @NonNull + public AssociationInfo build() { + if (mId <= 0) { + throw new IllegalArgumentException("Association ID should be greater than 0"); + } + if (mDeviceMacAddress == null && mDisplayName == null) { + throw new IllegalArgumentException("MAC address and the display name must NOT be " + + "null at the same time"); + } + return new AssociationInfo( + mId, + mUserId, + mPackageName, + mTag, + mDeviceMacAddress, + mDisplayName, + mDeviceProfile, + mAssociatedDevice, + mSelfManaged, + mNotifyOnDeviceNearby, + mRevoked, + mTimeApprovedMs, + mLastTimeConnectedMs, + mSystemDataSyncFlags + ); + } } } diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java index e931fe8526f1..1ef3d273fcb8 100644 --- a/core/java/android/content/res/Element.java +++ b/core/java/android/content/res/Element.java @@ -42,7 +42,7 @@ public class Element { public static final int MAX_ATTR_LEN_PATH = 4000; public static final int MAX_ATTR_LEN_DATA_VALUE = 4000; - private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?-%^*|/\\"; + private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?%^*|/\\"; private static final String TAG = "PackageParsing"; protected static final String TAG_ACTION = "action"; diff --git a/core/java/android/hardware/camera2/camera_platform.aconfig b/core/java/android/hardware/camera2/camera_platform.aconfig new file mode 100644 index 000000000000..67f63001ab58 --- /dev/null +++ b/core/java/android/hardware/camera2/camera_platform.aconfig @@ -0,0 +1,8 @@ +package: "com.android.hardware.camera2" + +flag { + namespace: "camera_platform" + name: "initial_test_flag" + description: "Flag infrastructure test flag" + bug: "292631208" +} diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 3812d37a5fed..2761aaeb4a7d 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Matrix; +import android.graphics.Rect; import android.graphics.Region; import android.gui.TouchOcclusionMode; import android.os.IBinder; @@ -103,10 +104,7 @@ public final class InputWindowHandle { public long dispatchingTimeoutMillis; // Window frame. - public int frameLeft; - public int frameTop; - public int frameRight; - public int frameBottom; + public final Rect frame = new Rect(); public int surfaceInset; @@ -184,10 +182,7 @@ public final class InputWindowHandle { layoutParamsFlags = other.layoutParamsFlags; layoutParamsType = other.layoutParamsType; dispatchingTimeoutMillis = other.dispatchingTimeoutMillis; - frameLeft = other.frameLeft; - frameTop = other.frameTop; - frameRight = other.frameRight; - frameBottom = other.frameBottom; + frame.set(other.frame); surfaceInset = other.surfaceInset; scaleFactor = other.scaleFactor; touchableRegion.set(other.touchableRegion); @@ -209,8 +204,7 @@ public final class InputWindowHandle { @Override public String toString() { return new StringBuilder(name != null ? name : "") - .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",") - .append(frameRight).append(",").append(frameBottom).append("]") + .append(", frame=[").append(frame).append("]") .append(", touchableRegion=").append(touchableRegion) .append(", scaleFactor=").append(scaleFactor) .append(", transform=").append(transform) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index afe755924e08..8899f5cd91d4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12884,6 +12884,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * @return true if this TextView could be filled by an Autofill service. Note that disabled + * fields can still be filled. + */ + @UnsupportedAppUsage + boolean isTextAutofillable() { + return mText instanceof Editable && onCheckIsTextEditor(); + } + + /** * Returns true, only while processing a touch gesture, if the initial * touch down event caused focus to move to the text view and as a result * its selection changed. Only valid while processing the touch gesture @@ -13605,7 +13614,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void autofill(AutofillValue value) { - if (!isTextEditable()) { + if (!isTextAutofillable()) { Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this); return; } @@ -13621,7 +13630,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public @AutofillType int getAutofillType() { - return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE; + return isTextAutofillable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE; } /** @@ -13635,7 +13644,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override @Nullable public AutofillValue getAutofillValue() { - if (isTextEditable()) { + if (isTextAutofillable()) { final CharSequence text = TextUtils.trimToParcelableSize(getText()); return AutofillValue.forText(text); } diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java index be88e53ad507..6e9f04482313 100644 --- a/core/java/android/window/WindowInfosListenerForTest.java +++ b/core/java/android/window/WindowInfosListenerForTest.java @@ -161,10 +161,8 @@ public class WindowInfosListenerForTest { private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) { var windowInfos = new ArrayList<WindowInfo>(windowHandles.length); for (var handle : windowHandles) { - var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight, - handle.frameBottom); windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId, - bounds, handle.inputConfig, handle.transform)); + handle.frame, handle.inputConfig, handle.transform)); } return windowInfos; } diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index eb5f2977e221..ea3c70f5e60b 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -35,6 +35,7 @@ #include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" +#include "jni_common.h" namespace android { @@ -57,10 +58,7 @@ static struct { jfieldID layoutParamsFlags; jfieldID layoutParamsType; jfieldID dispatchingTimeoutMillis; - jfieldID frameLeft; - jfieldID frameTop; - jfieldID frameRight; - jfieldID frameBottom; + jfieldID frame; jfieldID surfaceInset; jfieldID scaleFactor; jfieldID touchableRegion; @@ -128,14 +126,11 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.dispatchingTimeout = std::chrono::milliseconds( env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis)); - mInfo.frameLeft = env->GetIntField(obj, - gInputWindowHandleClassInfo.frameLeft); - mInfo.frameTop = env->GetIntField(obj, - gInputWindowHandleClassInfo.frameTop); - mInfo.frameRight = env->GetIntField(obj, - gInputWindowHandleClassInfo.frameRight); - mInfo.frameBottom = env->GetIntField(obj, - gInputWindowHandleClassInfo.frameBottom); + + ScopedLocalRef<jobject> frameObj(env, + env->GetObjectField(obj, gInputWindowHandleClassInfo.frame)); + mInfo.frame = JNICommon::rectFromObj(env, frameObj.get()); + mInfo.surfaceInset = env->GetIntField(obj, gInputWindowHandleClassInfo.surfaceInset); mInfo.globalScaleFactor = env->GetFloatField(obj, @@ -283,13 +278,9 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn std::chrono::duration_cast<std::chrono::milliseconds>( windowInfo.dispatchingTimeout) .count()); - env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameLeft, - windowInfo.frameLeft); - env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameTop, windowInfo.frameTop); - env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameRight, - windowInfo.frameRight); - env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameBottom, - windowInfo.frameBottom); + ScopedLocalRef<jobject> rectObj(env, JNICommon::objFromRect(env, windowInfo.frame)); + env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.frame, rectObj.get()); + env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset, windowInfo.surfaceInset); env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor, @@ -400,17 +391,7 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutMillis, clazz, "dispatchingTimeoutMillis", "J"); - GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz, - "frameLeft", "I"); - - GET_FIELD_ID(gInputWindowHandleClassInfo.frameTop, clazz, - "frameTop", "I"); - - GET_FIELD_ID(gInputWindowHandleClassInfo.frameRight, clazz, - "frameRight", "I"); - - GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz, - "frameBottom", "I"); + GET_FIELD_ID(gInputWindowHandleClassInfo.frame, clazz, "frame", "Landroid/graphics/Rect;"); GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz, "surfaceInset", "I"); diff --git a/core/jni/jni_common.cpp b/core/jni/jni_common.cpp index 8d376cf2c7f9..b81c9b6eed95 100644 --- a/core/jni/jni_common.cpp +++ b/core/jni/jni_common.cpp @@ -26,6 +26,8 @@ namespace android { static struct { + jclass clazz; + jmethodID ctor; jfieldID bottom; jfieldID left; jfieldID right; @@ -40,8 +42,15 @@ Rect JNICommon::rectFromObj(JNIEnv* env, jobject rectObj) { return Rect(left, top, right, bottom); } +jobject JNICommon::objFromRect(JNIEnv* env, Rect rect) { + return env->NewObject(gRectClassInfo.clazz, gRectClassInfo.ctor, rect.left, rect.top, + rect.right, rect.bottom); +} + int register_jni_common(JNIEnv* env) { jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect"); + gRectClassInfo.clazz = MakeGlobalRefOrDie(env, rectClazz); + gRectClassInfo.ctor = GetMethodIDOrDie(env, rectClazz, "<init>", "(IIII)V"); gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I"); gRectClassInfo.left = GetFieldIDOrDie(env, rectClazz, "left", "I"); gRectClassInfo.right = GetFieldIDOrDie(env, rectClazz, "right", "I"); diff --git a/core/jni/jni_common.h b/core/jni/jni_common.h index a2bf6fb3f5e0..d670a7d6bd59 100644 --- a/core/jni/jni_common.h +++ b/core/jni/jni_common.h @@ -22,5 +22,6 @@ class Rect; class JNICommon { public: static Rect rectFromObj(JNIEnv* env, jobject rectObj); + static jobject objFromRect(JNIEnv* env, Rect rect); }; } // namespace android
\ No newline at end of file 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 74f830e137f1..8ae12c75b3ee 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 @@ -131,7 +131,7 @@ public class BubbleStackView extends FrameLayout private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150; - private static final float SCRIM_ALPHA = 0.6f; + private static final float SCRIM_ALPHA = 0.32f; /** Minimum alpha value for scrim when alpha is being changed via drag */ private static final float MIN_SCRIM_ALPHA_FOR_DRAG = 0.2f; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt new file mode 100644 index 000000000000..2719cd29009e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt @@ -0,0 +1,365 @@ +/* + * 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.wm.shell.common.pip + +import android.annotation.DrawableRes +import android.annotation.StringRes +import android.app.PendingIntent +import android.app.RemoteAction +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.drawable.Icon +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.media.session.PlaybackState +import android.os.Handler +import android.os.HandlerExecutor +import android.os.UserHandle +import com.android.wm.shell.R +import com.android.wm.shell.pip.PipUtils +import java.util.function.Consumer + +/** + * Interfaces with the [MediaSessionManager] to compose the right set of actions to show (only + * if there are no actions from the PiP activity itself). The active media controller is only set + * when there is a media session from the top PiP activity. + */ +class PipMediaController(private val mContext: Context, private val mMainHandler: Handler) { + /** + * A listener interface to receive notification on changes to the media actions. + */ + interface ActionListener { + /** + * Called when the media actions changed. + */ + fun onMediaActionsChanged(actions: List<RemoteAction?>?) + } + + /** + * A listener interface to receive notification on changes to the media metadata. + */ + interface MetadataListener { + /** + * Called when the media metadata changed. + */ + fun onMediaMetadataChanged(metadata: MediaMetadata?) + } + + /** + * A listener interface to receive notification on changes to the media session token. + */ + interface TokenListener { + /** + * Called when the media session token changed. + */ + fun onMediaSessionTokenChanged(token: MediaSession.Token?) + } + + private val mHandlerExecutor: HandlerExecutor = HandlerExecutor(mMainHandler) + private val mMediaSessionManager: MediaSessionManager? + private var mMediaController: MediaController? = null + private val mPauseAction: RemoteAction + private val mPlayAction: RemoteAction + private val mNextAction: RemoteAction + private val mPrevAction: RemoteAction + private val mMediaActionReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (mMediaController == null) { + // no active media session, bail early. + return + } + when (intent.action) { + ACTION_PLAY -> mMediaController!!.transportControls.play() + ACTION_PAUSE -> mMediaController!!.transportControls.pause() + ACTION_NEXT -> mMediaController!!.transportControls.skipToNext() + ACTION_PREV -> mMediaController!!.transportControls.skipToPrevious() + } + } + } + private val mPlaybackChangedListener: MediaController.Callback = + object : MediaController.Callback() { + override fun onPlaybackStateChanged(state: PlaybackState?) { + notifyActionsChanged() + } + + override fun onMetadataChanged(metadata: MediaMetadata?) { + notifyMetadataChanged(metadata) + } + } + private val mSessionsChangedListener = + MediaSessionManager.OnActiveSessionsChangedListener { controllers: List<MediaController>? -> + resolveActiveMediaController(controllers) + } + private val mActionListeners = ArrayList<ActionListener>() + private val mMetadataListeners = ArrayList<MetadataListener>() + private val mTokenListeners = ArrayList<TokenListener>() + + init { + val mediaControlFilter = IntentFilter() + mediaControlFilter.addAction(ACTION_PLAY) + mediaControlFilter.addAction(ACTION_PAUSE) + mediaControlFilter.addAction(ACTION_NEXT) + mediaControlFilter.addAction(ACTION_PREV) + mContext.registerReceiverForAllUsers( + mMediaActionReceiver, mediaControlFilter, + SYSTEMUI_PERMISSION, mMainHandler, Context.RECEIVER_EXPORTED + ) + + // Creates the standard media buttons that we may show. + mPauseAction = getDefaultRemoteAction( + R.string.pip_pause, + R.drawable.pip_ic_pause_white, ACTION_PAUSE + ) + mPlayAction = getDefaultRemoteAction( + R.string.pip_play, + R.drawable.pip_ic_play_arrow_white, ACTION_PLAY + ) + mNextAction = getDefaultRemoteAction( + R.string.pip_skip_to_next, + R.drawable.pip_ic_skip_next_white, ACTION_NEXT + ) + mPrevAction = getDefaultRemoteAction( + R.string.pip_skip_to_prev, + R.drawable.pip_ic_skip_previous_white, ACTION_PREV + ) + mMediaSessionManager = mContext.getSystemService( + MediaSessionManager::class.java + ) + } + + /** + * Handles when an activity is pinned. + */ + fun onActivityPinned() { + // Once we enter PiP, try to find the active media controller for the top most activity + resolveActiveMediaController( + mMediaSessionManager!!.getActiveSessionsForUser( + null, + UserHandle.CURRENT + ) + ) + } + + /** + * Adds a new media action listener. + */ + fun addActionListener(listener: ActionListener) { + if (!mActionListeners.contains(listener)) { + mActionListeners.add(listener) + listener.onMediaActionsChanged(mediaActions) + } + } + + /** + * Removes a media action listener. + */ + fun removeActionListener(listener: ActionListener) { + listener.onMediaActionsChanged(emptyList<RemoteAction>()) + mActionListeners.remove(listener) + } + + /** + * Adds a new media metadata listener. + */ + fun addMetadataListener(listener: MetadataListener) { + if (!mMetadataListeners.contains(listener)) { + mMetadataListeners.add(listener) + listener.onMediaMetadataChanged(mediaMetadata) + } + } + + /** + * Removes a media metadata listener. + */ + fun removeMetadataListener(listener: MetadataListener) { + listener.onMediaMetadataChanged(null) + mMetadataListeners.remove(listener) + } + + /** + * Adds a new token listener. + */ + fun addTokenListener(listener: TokenListener) { + if (!mTokenListeners.contains(listener)) { + mTokenListeners.add(listener) + listener.onMediaSessionTokenChanged(token) + } + } + + /** + * Removes a token listener. + */ + fun removeTokenListener(listener: TokenListener) { + listener.onMediaSessionTokenChanged(null) + mTokenListeners.remove(listener) + } + + private val token: MediaSession.Token? + get() = if (mMediaController == null) { + null + } else mMediaController!!.sessionToken + private val mediaMetadata: MediaMetadata? + get() = if (mMediaController != null) mMediaController!!.metadata else null + + private val mediaActions: List<RemoteAction?> + /** + * Gets the set of media actions currently available. + */ + get() { + if (mMediaController == null) { + return emptyList<RemoteAction>() + } + // Cache the PlaybackState since it's a Binder call. + // Safe because mMediaController is guaranteed non-null here. + val playbackState: PlaybackState = mMediaController!!.playbackState + ?: return emptyList<RemoteAction>() + val mediaActions = ArrayList<RemoteAction?>() + val isPlaying = playbackState.isActive + val actions = playbackState.actions + + // Prev action + mPrevAction.isEnabled = + actions and PlaybackState.ACTION_SKIP_TO_PREVIOUS != 0L + mediaActions.add(mPrevAction) + + // Play/pause action + if (!isPlaying && actions and PlaybackState.ACTION_PLAY != 0L) { + mediaActions.add(mPlayAction) + } else if (isPlaying && actions and PlaybackState.ACTION_PAUSE != 0L) { + mediaActions.add(mPauseAction) + } + + // Next action + mNextAction.isEnabled = + actions and PlaybackState.ACTION_SKIP_TO_NEXT != 0L + mediaActions.add(mNextAction) + return mediaActions + } + + /** @return Default [RemoteAction] sends broadcast back to SysUI. + */ + private fun getDefaultRemoteAction( + @StringRes titleAndDescription: Int, + @DrawableRes icon: Int, + action: String + ): RemoteAction { + val titleAndDescriptionStr = mContext.getString(titleAndDescription) + val intent = Intent(action) + intent.setPackage(mContext.packageName) + return RemoteAction( + Icon.createWithResource(mContext, icon), + titleAndDescriptionStr, titleAndDescriptionStr, + PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + ) + } + + /** + * Re-registers the session listener for the current user. + */ + fun registerSessionListenerForCurrentUser() { + mMediaSessionManager!!.removeOnActiveSessionsChangedListener(mSessionsChangedListener) + mMediaSessionManager.addOnActiveSessionsChangedListener( + null, UserHandle.CURRENT, + mHandlerExecutor, mSessionsChangedListener + ) + } + + /** + * Tries to find and set the active media controller for the top PiP activity. + */ + private fun resolveActiveMediaController(controllers: List<MediaController>?) { + if (controllers != null) { + val topActivity = PipUtils.getTopPipActivity(mContext).first + if (topActivity != null) { + for (i in controllers.indices) { + val controller = controllers[i] + if (controller.packageName == topActivity.packageName) { + setActiveMediaController(controller) + return + } + } + } + } + setActiveMediaController(null) + } + + /** + * Sets the active media controller for the top PiP activity. + */ + private fun setActiveMediaController(controller: MediaController?) { + if (controller != mMediaController) { + if (mMediaController != null) { + mMediaController!!.unregisterCallback(mPlaybackChangedListener) + } + mMediaController = controller + controller?.registerCallback(mPlaybackChangedListener, mMainHandler) + notifyActionsChanged() + notifyMetadataChanged(mediaMetadata) + notifyTokenChanged(token) + + // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV) + } + } + + /** + * Notifies all listeners that the actions have changed. + */ + private fun notifyActionsChanged() { + if (mActionListeners.isNotEmpty()) { + val actions = mediaActions + mActionListeners.forEach( + Consumer { l: ActionListener -> l.onMediaActionsChanged(actions) }) + } + } + + /** + * Notifies all listeners that the metadata have changed. + */ + private fun notifyMetadataChanged(metadata: MediaMetadata?) { + if (mMetadataListeners.isNotEmpty()) { + mMetadataListeners.forEach(Consumer { l: MetadataListener -> + l.onMediaMetadataChanged( + metadata + ) + }) + } + } + + private fun notifyTokenChanged(token: MediaSession.Token?) { + if (mTokenListeners.isNotEmpty()) { + mTokenListeners.forEach(Consumer { l: TokenListener -> + l.onMediaSessionTokenChanged( + token + ) + }) + } + } + + companion object { + private const val SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF" + private const val ACTION_PLAY = "com.android.wm.shell.pip.PLAY" + private const val ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE" + private const val ACTION_NEXT = "com.android.wm.shell.pip.NEXT" + private const val ACTION_PREV = "com.android.wm.shell.pip.PREV" + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index a2eabb150d69..94723416cdf8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -58,6 +58,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.compatui.CompatUIConfiguration; import com.android.wm.shell.compatui.CompatUIController; @@ -343,6 +344,13 @@ public abstract class WMShellBaseModule { return new PipUiEventLogger(uiEventLogger, packageManager); } + @WMSingleton + @Provides + static PipMediaController providePipMediaController(Context context, + @ShellMainThread Handler mainHandler) { + return new PipMediaController(context, mainHandler); + } + // // Bubbles (optional feature) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java index 5fd4f24a0f0d..24ef44a7c0f4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java @@ -32,6 +32,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.PhoneSizeSpecSource; import com.android.wm.shell.common.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; @@ -42,7 +43,6 @@ import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java index 55a810ae5c68..c4ca5013afb6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java @@ -17,11 +17,8 @@ package com.android.wm.shell.dagger.pip; import android.content.Context; -import android.os.Handler; -import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.dagger.WMSingleton; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import dagger.Module; @@ -33,14 +30,6 @@ import dagger.Provides; */ @Module public abstract class Pip1SharedModule { - // Needs handler for registering broadcast receivers - @WMSingleton - @Provides - static PipMediaController providePipMediaController(Context context, - @ShellMainThread Handler mainHandler) { - return new PipMediaController(context, mainHandler); - } - @WMSingleton @Provides static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java index 4e332becb2f9..a6ff9ecf7f4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java @@ -30,6 +30,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.LegacySizeSpecSource; import com.android.wm.shell.common.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; @@ -37,7 +38,6 @@ import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipDisplayLayoutState; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java deleted file mode 100644 index ddffb5bdacde..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.pip; - -import static android.app.PendingIntent.FLAG_IMMUTABLE; -import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; - -import android.annotation.DrawableRes; -import android.annotation.StringRes; -import android.annotation.SuppressLint; -import android.app.PendingIntent; -import android.app.RemoteAction; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.drawable.Icon; -import android.media.MediaMetadata; -import android.media.session.MediaController; -import android.media.session.MediaSession; -import android.media.session.MediaSessionManager; -import android.media.session.PlaybackState; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.UserHandle; - -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Interfaces with the {@link MediaSessionManager} to compose the right set of actions to show (only - * if there are no actions from the PiP activity itself). The active media controller is only set - * when there is a media session from the top PiP activity. - */ -public class PipMediaController { - private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"; - - private static final String ACTION_PLAY = "com.android.wm.shell.pip.PLAY"; - private static final String ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE"; - private static final String ACTION_NEXT = "com.android.wm.shell.pip.NEXT"; - private static final String ACTION_PREV = "com.android.wm.shell.pip.PREV"; - - /** - * A listener interface to receive notification on changes to the media actions. - */ - public interface ActionListener { - /** - * Called when the media actions changed. - */ - void onMediaActionsChanged(List<RemoteAction> actions); - } - - /** - * A listener interface to receive notification on changes to the media metadata. - */ - public interface MetadataListener { - /** - * Called when the media metadata changed. - */ - void onMediaMetadataChanged(MediaMetadata metadata); - } - - /** - * A listener interface to receive notification on changes to the media session token. - */ - public interface TokenListener { - /** - * Called when the media session token changed. - */ - void onMediaSessionTokenChanged(MediaSession.Token token); - } - - private final Context mContext; - private final Handler mMainHandler; - private final HandlerExecutor mHandlerExecutor; - - private final MediaSessionManager mMediaSessionManager; - private MediaController mMediaController; - - private RemoteAction mPauseAction; - private RemoteAction mPlayAction; - private RemoteAction mNextAction; - private RemoteAction mPrevAction; - - private final BroadcastReceiver mMediaActionReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (mMediaController == null || mMediaController.getTransportControls() == null) { - // no active media session, bail early. - return; - } - switch (intent.getAction()) { - case ACTION_PLAY: - mMediaController.getTransportControls().play(); - break; - case ACTION_PAUSE: - mMediaController.getTransportControls().pause(); - break; - case ACTION_NEXT: - mMediaController.getTransportControls().skipToNext(); - break; - case ACTION_PREV: - mMediaController.getTransportControls().skipToPrevious(); - break; - } - } - }; - - private final MediaController.Callback mPlaybackChangedListener = - new MediaController.Callback() { - @Override - public void onPlaybackStateChanged(PlaybackState state) { - notifyActionsChanged(); - } - - @Override - public void onMetadataChanged(@Nullable MediaMetadata metadata) { - notifyMetadataChanged(metadata); - } - }; - - private final MediaSessionManager.OnActiveSessionsChangedListener mSessionsChangedListener = - this::resolveActiveMediaController; - - private final ArrayList<ActionListener> mActionListeners = new ArrayList<>(); - private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>(); - private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>(); - - public PipMediaController(Context context, Handler mainHandler) { - mContext = context; - mMainHandler = mainHandler; - mHandlerExecutor = new HandlerExecutor(mMainHandler); - if (!PipUtils.isPip2ExperimentEnabled()) { - IntentFilter mediaControlFilter = new IntentFilter(); - mediaControlFilter.addAction(ACTION_PLAY); - mediaControlFilter.addAction(ACTION_PAUSE); - mediaControlFilter.addAction(ACTION_NEXT); - mediaControlFilter.addAction(ACTION_PREV); - mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter, - SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED); - } - - // Creates the standard media buttons that we may show. - mPauseAction = getDefaultRemoteAction(R.string.pip_pause, - R.drawable.pip_ic_pause_white, ACTION_PAUSE); - mPlayAction = getDefaultRemoteAction(R.string.pip_play, - R.drawable.pip_ic_play_arrow_white, ACTION_PLAY); - mNextAction = getDefaultRemoteAction(R.string.pip_skip_to_next, - R.drawable.pip_ic_skip_next_white, ACTION_NEXT); - mPrevAction = getDefaultRemoteAction(R.string.pip_skip_to_prev, - R.drawable.pip_ic_skip_previous_white, ACTION_PREV); - - mMediaSessionManager = context.getSystemService(MediaSessionManager.class); - } - - /** - * Handles when an activity is pinned. - */ - public void onActivityPinned() { - // Once we enter PiP, try to find the active media controller for the top most activity - resolveActiveMediaController(mMediaSessionManager.getActiveSessionsForUser(null, - UserHandle.CURRENT)); - } - - /** - * Adds a new media action listener. - */ - public void addActionListener(ActionListener listener) { - if (!mActionListeners.contains(listener)) { - mActionListeners.add(listener); - listener.onMediaActionsChanged(getMediaActions()); - } - } - - /** - * Removes a media action listener. - */ - public void removeActionListener(ActionListener listener) { - listener.onMediaActionsChanged(Collections.emptyList()); - mActionListeners.remove(listener); - } - - /** - * Adds a new media metadata listener. - */ - public void addMetadataListener(MetadataListener listener) { - if (!mMetadataListeners.contains(listener)) { - mMetadataListeners.add(listener); - listener.onMediaMetadataChanged(getMediaMetadata()); - } - } - - /** - * Removes a media metadata listener. - */ - public void removeMetadataListener(MetadataListener listener) { - listener.onMediaMetadataChanged(null); - mMetadataListeners.remove(listener); - } - - /** - * Adds a new token listener. - */ - public void addTokenListener(TokenListener listener) { - if (!mTokenListeners.contains(listener)) { - mTokenListeners.add(listener); - listener.onMediaSessionTokenChanged(getToken()); - } - } - - /** - * Removes a token listener. - */ - public void removeTokenListener(TokenListener listener) { - listener.onMediaSessionTokenChanged(null); - mTokenListeners.remove(listener); - } - - private MediaSession.Token getToken() { - if (mMediaController == null) { - return null; - } - return mMediaController.getSessionToken(); - } - - private MediaMetadata getMediaMetadata() { - return mMediaController != null ? mMediaController.getMetadata() : null; - } - - /** - * Gets the set of media actions currently available. - */ - // This is due to using PlaybackState#isActive, which is added in API 31. - // It can be removed when min_sdk of the app is set to 31 or greater. - @SuppressLint("NewApi") - private List<RemoteAction> getMediaActions() { - // Cache the PlaybackState since it's a Binder call. - final PlaybackState playbackState; - if (mMediaController == null - || (playbackState = mMediaController.getPlaybackState()) == null) { - return Collections.emptyList(); - } - - ArrayList<RemoteAction> mediaActions = new ArrayList<>(); - boolean isPlaying = playbackState.isActive(); - long actions = playbackState.getActions(); - - // Prev action - mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0); - mediaActions.add(mPrevAction); - - // Play/pause action - if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) { - mediaActions.add(mPlayAction); - } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) { - mediaActions.add(mPauseAction); - } - - // Next action - mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0); - mediaActions.add(mNextAction); - return mediaActions; - } - - /** @return Default {@link RemoteAction} sends broadcast back to SysUI. */ - private RemoteAction getDefaultRemoteAction(@StringRes int titleAndDescription, - @DrawableRes int icon, String action) { - final String titleAndDescriptionStr = mContext.getString(titleAndDescription); - final Intent intent = new Intent(action); - intent.setPackage(mContext.getPackageName()); - return new RemoteAction(Icon.createWithResource(mContext, icon), - titleAndDescriptionStr, titleAndDescriptionStr, - PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, - FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } - - /** - * Re-registers the session listener for the current user. - */ - public void registerSessionListenerForCurrentUser() { - mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener); - mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT, - mHandlerExecutor, mSessionsChangedListener); - } - - /** - * Tries to find and set the active media controller for the top PiP activity. - */ - private void resolveActiveMediaController(List<MediaController> controllers) { - if (controllers != null) { - final ComponentName topActivity = PipUtils.getTopPipActivity(mContext).first; - if (topActivity != null) { - for (int i = 0; i < controllers.size(); i++) { - final MediaController controller = controllers.get(i); - if (controller.getPackageName().equals(topActivity.getPackageName())) { - setActiveMediaController(controller); - return; - } - } - } - } - setActiveMediaController(null); - } - - /** - * Sets the active media controller for the top PiP activity. - */ - private void setActiveMediaController(MediaController controller) { - if (controller != mMediaController) { - if (mMediaController != null) { - mMediaController.unregisterCallback(mPlaybackChangedListener); - } - mMediaController = controller; - if (controller != null) { - controller.registerCallback(mPlaybackChangedListener, mMainHandler); - } - notifyActionsChanged(); - notifyMetadataChanged(getMediaMetadata()); - notifyTokenChanged(getToken()); - - // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV) - } - } - - /** - * Notifies all listeners that the actions have changed. - */ - private void notifyActionsChanged() { - if (!mActionListeners.isEmpty()) { - List<RemoteAction> actions = getMediaActions(); - mActionListeners.forEach(l -> l.onMediaActionsChanged(actions)); - } - } - - /** - * Notifies all listeners that the metadata have changed. - */ - private void notifyMetadataChanged(MediaMetadata metadata) { - if (!mMetadataListeners.isEmpty()) { - mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata)); - } - } - - private void notifyTokenChanged(MediaSession.Token token) { - if (!mTokenListeners.isEmpty()) { - mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token)); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index c93d85016df6..cc182ba89985 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -38,10 +38,10 @@ import android.view.WindowManagerGlobal; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipMediaController.ActionListener; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.pip.PipBoundsState; -import com.android.wm.shell.pip.PipMediaController; -import com.android.wm.shell.pip.PipMediaController.ActionListener; import com.android.wm.shell.pip.PipMenuController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index f25110ac3257..5c65d7839f07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -76,6 +76,7 @@ import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.IPip; @@ -87,7 +88,6 @@ import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java index 11c2665c9665..f1606f6a8ca5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java @@ -35,7 +35,7 @@ import android.content.Context; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; -import com.android.wm.shell.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index e3544c63dd6e..5f5d8ad588dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -48,11 +48,11 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipDisplayLayoutState; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java index f22ee595e6c9..39c7a4b220a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java @@ -36,7 +36,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ImageUtils; import com.android.wm.shell.R; -import com.android.wm.shell.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 05c6ba9f0897..911f5e165ef1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -56,12 +56,12 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; -import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java index ec84d7e20714..45f6c8c7f69f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java @@ -38,7 +38,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipMediaController; import org.junit.Before; import org.junit.Test; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index c25df6e08fd0..c571b742d2d1 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -144,6 +144,8 @@ APerformanceHintSession* APerformanceHintManager::createSession( binder::Status ret = mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session); if (!ret.isOk() || !session) { + ALOGE("%s: PerformanceHint cannot create hint session. %s", __FUNCTION__, + ret.exceptionMessage().c_str()); return nullptr; } return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java index 745591235dbb..4fcdc8b24357 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java @@ -27,6 +27,7 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.R; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.utils.ThreadUtils; /** * Preference controller for bluetooth address @@ -74,13 +75,18 @@ public abstract class AbstractBluetoothAddressPreferenceController protected void updateConnectivity() { BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter(); if (bluetooth != null && mBtAddress != null) { - String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null; - if (!TextUtils.isEmpty(address)) { - // Convert the address to lowercase for consistency with the wifi MAC address. - mBtAddress.setSummary(address.toLowerCase()); - } else { - mBtAddress.setSummary(R.string.status_unavailable); - } + ThreadUtils.postOnBackgroundThread(() -> { + String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null; + ThreadUtils.postOnMainThread(() -> { + if (!TextUtils.isEmpty(address)) { + // Convert the address to lowercase for consistency with the wifi MAC + // address. + mBtAddress.setSummary(address.toLowerCase()); + } else { + mBtAddress.setSummary(R.string.status_unavailable); + } + }); + }); } } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index d1f7f2fe3516..fa2d677e85c2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -24,7 +24,7 @@ public class GlobalSettings { /** * These keys may be mentioned in the SETTINGS_TO_BACKUP arrays in SystemSettings * and SecureSettings as well. This is because those tables drive both backup and - * restore, and restore needs to properly whitelist keys that used to live + * restore, and restore needs to properly allowlist keys that used to live * in those namespaces. * * NOTE: Settings are backed up and restored in the order they appear diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index c830d6b2b611..7b49608ceb7e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -927,7 +927,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { @VisibleForTesting SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) { // Figure out the white list and redirects to the global table. We restore anything - // in either the backup whitelist or the legacy-restore whitelist for this table. + // in either the backup allowlist or the legacy-restore allowlist for this table. String[] whitelist; Map<String, Validator> validators = null; if (contentUri.equals(Settings.Secure.CONTENT_URI)) { @@ -1474,7 +1474,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { } /** - * Store the whitelist of settings to be backed up and validators for them. + * Store the allowlist of settings to be backed up and validators for them. */ @VisibleForTesting static class SettingsBackupWhitelist { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 7e9795643849..19d828b679b0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -251,7 +251,7 @@ public class SettingsProvider extends ContentProvider { public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1; public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L; - // Overlay specified settings whitelisted for Instant Apps + // Overlay specified settings allowlisted for Instant Apps private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>(); private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>(); private static final Set<String> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS = new ArraySet<>(); @@ -2155,7 +2155,7 @@ public class SettingsProvider extends ContentProvider { @GuardedBy("mLock") private List<String> getSettingsNamesLocked(int settingsType, int userId) { - // Don't enforce the instant app whitelist for now -- its too prone to unintended breakage + // Don't enforce the instant app allowlist for now -- its too prone to unintended breakage // in the current form. return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId); } @@ -2196,7 +2196,7 @@ public class SettingsProvider extends ContentProvider { } if (!getInstantAppAccessibleSettings(settingsType).contains(settingName) && !getOverlayInstantAppAccessibleSettings(settingsType).contains(settingName)) { - // Don't enforce the instant app whitelist for now -- its too prone to unintended + // Don't enforce the instant app allowlist for now -- its too prone to unintended // breakage in the current form. Slog.w(LOG_TAG, "Instant App " + ai.packageName + " trying to access unexposed setting, this will be an error in the future."); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 73ead3c4d61f..55527455ca11 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -1110,7 +1110,10 @@ final class SettingsState { } catch (FileNotFoundException fnfe) { final String message = "No fallback file found for: " + mStatePersistFile; Slog.wtf(LOG_TAG, message); - throw new IllegalStateException(message); + if (!isConfigSettingsKey(mKey)) { + // Allow partially deserialized config settings because they can be updated later + throw new IllegalStateException(message); + } } if (parseStateFromXmlStreamLocked(in)) { // Parsed state from fallback file. Restore original file with fallback file @@ -1122,7 +1125,10 @@ final class SettingsState { } else { final String message = "Failed parsing settings file: " + mStatePersistFile; Slog.wtf(LOG_TAG, message); - throw new IllegalStateException(message); + if (!isConfigSettingsKey(mKey)) { + // Allow partially deserialized config settings because they can be updated later + throw new IllegalStateException(message); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 58c8000a2328..802a550c4d29 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -73,9 +73,7 @@ import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteracto import com.android.systemui.biometrics.domain.model.BiometricModalities; import com.android.systemui.biometrics.ui.BiometricPromptLayout; import com.android.systemui.biometrics.ui.CredentialView; -import com.android.systemui.biometrics.ui.binder.AuthBiometricFingerprintViewBinder; import com.android.systemui.biometrics.ui.binder.BiometricViewBinder; -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.dagger.qualifiers.Background; @@ -142,8 +140,6 @@ public class AuthContainerView extends LinearLayout // TODO: these should be migrated out once ready private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor; - private final Provider<AuthBiometricFingerprintViewModel> - mAuthBiometricFingerprintViewModelProvider; private final @NonNull Provider<PromptSelectorInteractor> mPromptSelectorInteractorProvider; // TODO(b/251476085): these should be migrated out of the view private final Provider<CredentialViewModel> mCredentialViewModelProvider; @@ -283,8 +279,6 @@ public class AuthContainerView extends LinearLayout @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull InteractionJankMonitor jankMonitor, - @NonNull Provider<AuthBiometricFingerprintViewModel> - authBiometricFingerprintViewModelProvider, @NonNull Provider<PromptCredentialInteractor> promptCredentialInteractor, @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor, @NonNull PromptViewModel promptViewModel, @@ -293,9 +287,9 @@ public class AuthContainerView extends LinearLayout @NonNull VibratorHelper vibratorHelper) { this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, - jankMonitor, authBiometricFingerprintViewModelProvider, promptSelectorInteractor, - promptCredentialInteractor, promptViewModel, credentialViewModelProvider, - new Handler(Looper.getMainLooper()), bgExecutor, vibratorHelper); + jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel, + credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor, + vibratorHelper); } @VisibleForTesting @@ -309,8 +303,6 @@ public class AuthContainerView extends LinearLayout @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull InteractionJankMonitor jankMonitor, - @NonNull Provider<AuthBiometricFingerprintViewModel> - authBiometricFingerprintViewModelProvider, @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider, @NonNull Provider<PromptCredentialInteractor> credentialInteractor, @NonNull PromptViewModel promptViewModel, @@ -359,7 +351,6 @@ public class AuthContainerView extends LinearLayout mBackgroundExecutor = bgExecutor; mInteractionJankMonitor = jankMonitor; mPromptCredentialInteractor = credentialInteractor; - mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; mCredentialViewModelProvider = credentialViewModelProvider; mPromptViewModel = promptViewModel; @@ -442,9 +433,6 @@ public class AuthContainerView extends LinearLayout fingerprintAndFaceView.updateOverrideIconLayoutParamsSize(); fingerprintAndFaceView.setFaceClass3( faceProperties.sensorStrength == STRENGTH_STRONG); - final AuthBiometricFingerprintViewModel fpAndFaceViewModel = - mAuthBiometricFingerprintViewModelProvider.get(); - AuthBiometricFingerprintViewBinder.bind(fingerprintAndFaceView, fpAndFaceViewModel); mBiometricView = fingerprintAndFaceView; } else if (fpProperties != null) { final AuthBiometricFingerprintView fpView = @@ -453,9 +441,6 @@ public class AuthContainerView extends LinearLayout fpView.setSensorProperties(fpProperties); fpView.setScaleFactorProvider(config.mScaleProvider); fpView.updateOverrideIconLayoutParamsSize(); - final AuthBiometricFingerprintViewModel fpViewModel = - mAuthBiometricFingerprintViewModelProvider.get(); - AuthBiometricFingerprintViewBinder.bind(fpView, fpViewModel); mBiometricView = fpView; } else if (faceProperties != null) { mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 7b288a8d49f1..d5289a49be51 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -73,7 +73,6 @@ import com.android.systemui.CoreStartable; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.dagger.SysUISingleton; @@ -134,8 +133,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, private final CoroutineScope mApplicationCoroutineScope; // TODO: these should be migrated out once ready - @NonNull private final Provider<AuthBiometricFingerprintViewModel> - mAuthBiometricFingerprintViewModelProvider; @NonNull private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor; @NonNull private final Provider<PromptSelectorInteractor> mPromptSelectorInteractor; @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider; @@ -765,8 +762,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @NonNull LockPatternUtils lockPatternUtils, @NonNull UdfpsLogger udfpsLogger, @NonNull LogContextInteractor logContextInteractor, - @NonNull Provider<AuthBiometricFingerprintViewModel> - authBiometricFingerprintViewModelProvider, @NonNull Provider<PromptCredentialInteractor> promptCredentialInteractorProvider, @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider, @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @@ -801,7 +796,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, mVibratorHelper = vibratorHelper; mLogContextInteractor = logContextInteractor; - mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider; mPromptSelectorInteractor = promptSelectorInteractorProvider; mPromptCredentialInteractor = promptCredentialInteractorProvider; mPromptViewModelProvider = promptViewModelProvider; @@ -1344,9 +1338,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, config.mScaleProvider = this::getScaleFactor; return new AuthContainerView(config, mFeatureFlags, mApplicationCoroutineScope, mFpProps, mFaceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, - mInteractionJankMonitor, mAuthBiometricFingerprintViewModelProvider, - mPromptCredentialInteractor, mPromptSelectorInteractor, viewModel, - mCredentialViewModelProvider, bgExecutor, mVibratorHelper); + mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor, + viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt index bb87dca1cdc4..5badcaf06003 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt @@ -21,15 +21,18 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.Utils.getCredentialType import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed +import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.PromptRepository import com.android.systemui.biometrics.domain.model.BiometricModalities import com.android.systemui.biometrics.domain.model.BiometricOperationInfo import com.android.systemui.biometrics.domain.model.BiometricPromptRequest import com.android.systemui.biometrics.shared.model.BiometricUserInfo +import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -65,6 +68,9 @@ interface PromptSelectorInteractor { */ val isConfirmationRequired: Flow<Boolean> + /** Fingerprint sensor type */ + val sensorType: StateFlow<FingerprintSensorType> + /** Use biometrics for authentication. */ fun useBiometricsForAuthentication( promptInfo: PromptInfo, @@ -89,6 +95,7 @@ interface PromptSelectorInteractor { class PromptSelectorInteractorImpl @Inject constructor( + private val fingerprintPropertyRepository: FingerprintPropertyRepository, private val promptRepository: PromptRepository, lockPatternUtils: LockPatternUtils, ) : PromptSelectorInteractor { @@ -140,6 +147,9 @@ constructor( } } + override val sensorType: StateFlow<FingerprintSensorType> = + fingerprintPropertyRepository.sensorType + override fun useBiometricsForAuthentication( promptInfo: PromptInfo, userId: Int, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt deleted file mode 100644 index 9c1bcec2f396..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.android.systemui.biometrics.ui.binder - -import com.android.systemui.biometrics.AuthBiometricFingerprintView -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel - -object AuthBiometricFingerprintViewBinder { - - /** - * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel]. - */ - @JvmStatic - fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) { - if (view.isSfps) { - AuthBiometricFingerprintIconViewBinder.bind(view.getIconView(), viewModel) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index d054751b760b..b1439fd7421d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -108,6 +108,9 @@ object BiometricViewBinder { val iconViewOverlay = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay) val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon) + + PromptFingerprintIconViewBinder.bind(iconView, viewModel.fingerprintIconViewModel) + val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator) // Negative-side (left) buttons diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt index bd0907e588ca..188c82b52374 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt @@ -21,26 +21,29 @@ import android.view.DisplayInfo import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView -import com.android.systemui.biometrics.AuthBiometricFingerprintView -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel +import com.android.systemui.biometrics.ui.viewmodel.PromptFingerprintIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.launch -/** Sub-binder for [AuthBiometricFingerprintView.mIconView]. */ -object AuthBiometricFingerprintIconViewBinder { +/** Sub-binder for [BiometricPromptLayout.iconView]. */ +object PromptFingerprintIconViewBinder { - /** - * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel]. - */ + /** Binds [BiometricPromptLayout.iconView] to [PromptFingerprintIconViewModel]. */ @JvmStatic - fun bind(view: LottieAnimationView, viewModel: AuthBiometricFingerprintViewModel) { + fun bind(view: LottieAnimationView, viewModel: PromptFingerprintIconViewModel) { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { val displayInfo = DisplayInfo() view.context.display?.getDisplayInfo(displayInfo) viewModel.setRotation(displayInfo.rotation) viewModel.onConfigurationChanged(view.context.resources.configuration) - launch { viewModel.iconAsset.collect { iconAsset -> view.setAnimation(iconAsset) } } + launch { + viewModel.iconAsset.collect { iconAsset -> + if (iconAsset != -1) { + view.setAnimation(iconAsset) + } + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt index 617d80cee09d..9b30acb84428 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt @@ -22,23 +22,35 @@ import android.content.res.Configuration import android.view.Surface import com.android.systemui.R import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.shared.model.FingerprintSensorType import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -/** Models UI of AuthBiometricFingerprintView to support rear display state changes. */ -class AuthBiometricFingerprintViewModel +/** Models UI of [BiometricPromptLayout.iconView] */ +class PromptFingerprintIconViewModel @Inject -constructor(private val interactor: DisplayStateInteractor) { +constructor( + private val displayStateInteractor: DisplayStateInteractor, + private val promptSelectorInteractor: PromptSelectorInteractor, +) { /** Current device rotation. */ private var rotation: Int = Surface.ROTATION_0 - /** Current AuthBiometricFingerprintView asset. */ + /** Current BiometricPromptLayout.iconView asset. */ val iconAsset: Flow<Int> = - combine(interactor.isFolded, interactor.isInRearDisplayMode) { - isFolded: Boolean, - isInRearDisplayMode: Boolean -> - getSideFpsAnimationAsset(isFolded, isInRearDisplayMode) + combine( + displayStateInteractor.isFolded, + displayStateInteractor.isInRearDisplayMode, + promptSelectorInteractor.sensorType, + ) { isFolded: Boolean, isInRearDisplayMode: Boolean, sensorType: FingerprintSensorType -> + when (sensorType) { + FingerprintSensorType.POWER_BUTTON -> + getSideFpsAnimationAsset(isFolded, isInRearDisplayMode) + // Replace below when non-SFPS iconAsset logic is migrated to this ViewModel + else -> -1 + } } @RawRes @@ -75,7 +87,7 @@ constructor(private val interactor: DisplayStateInteractor) { /** Called on configuration changes */ fun onConfigurationChanged(newConfig: Configuration) { - interactor.onConfigurationChanged(newConfig) + displayStateInteractor.onConfigurationChanged(newConfig) } fun setRotation(newRotation: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 89561a5a212b..4dc7720ef447 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -20,6 +20,7 @@ import android.util.Log import android.view.HapticFeedbackConstants import android.view.MotionEvent import com.android.systemui.biometrics.AuthBiometricView +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality @@ -45,16 +46,23 @@ import kotlinx.coroutines.launch class PromptViewModel @Inject constructor( - private val interactor: PromptSelectorInteractor, + private val displayStateInteractor: DisplayStateInteractor, + private val promptSelectorInteractor: PromptSelectorInteractor, private val vibrator: VibratorHelper, private val featureFlags: FeatureFlags, ) { + /** Models UI of [BiometricPromptLayout.iconView] */ + val fingerprintIconViewModel: PromptFingerprintIconViewModel = + PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor) + /** The set of modalities available for this prompt */ val modalities: Flow<BiometricModalities> = - interactor.prompt.map { it?.modalities ?: BiometricModalities() }.distinctUntilChanged() + promptSelectorInteractor.prompt + .map { it?.modalities ?: BiometricModalities() } + .distinctUntilChanged() // TODO(b/251476085): remove after icon controllers are migrated - do not keep this state - private var _legacyState = MutableStateFlow(AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN) + private var _legacyState = MutableStateFlow(AuthBiometricView.STATE_IDLE) val legacyState: StateFlow<Int> = _legacyState.asStateFlow() private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false) @@ -75,17 +83,18 @@ constructor( * successful authentication. */ val isConfirmationRequired: Flow<Boolean> = - combine(_isOverlayTouched, interactor.isConfirmationRequired) { + combine(_isOverlayTouched, promptSelectorInteractor.isConfirmationRequired) { isOverlayTouched, isConfirmationRequired -> !isOverlayTouched && isConfirmationRequired } /** The kind of credential the user has. */ - val credentialKind: Flow<PromptKind> = interactor.credentialKind + val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind /** The label to use for the cancel button. */ - val negativeButtonText: Flow<String> = interactor.prompt.map { it?.negativeButtonText ?: "" } + val negativeButtonText: Flow<String> = + promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" } private val _message: MutableStateFlow<PromptMessage> = MutableStateFlow(PromptMessage.Empty) @@ -113,7 +122,7 @@ constructor( _forceLargeSize, _forceMediumSize, modalities, - interactor.isConfirmationRequired, + promptSelectorInteractor.isConfirmationRequired, fingerprintStartMode, ) { forceLarge, forceMedium, modalities, confirmationRequired, fpStartMode -> when { @@ -129,14 +138,16 @@ constructor( .distinctUntilChanged() /** Title for the prompt. */ - val title: Flow<String> = interactor.prompt.map { it?.title ?: "" }.distinctUntilChanged() + val title: Flow<String> = + promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged() /** Subtitle for the prompt. */ - val subtitle: Flow<String> = interactor.prompt.map { it?.subtitle ?: "" }.distinctUntilChanged() + val subtitle: Flow<String> = + promptSelectorInteractor.prompt.map { it?.subtitle ?: "" }.distinctUntilChanged() /** Description for the prompt. */ val description: Flow<String> = - interactor.prompt.map { it?.description ?: "" }.distinctUntilChanged() + promptSelectorInteractor.prompt.map { it?.description ?: "" }.distinctUntilChanged() /** If the indicator (help, error) message should be shown. */ val isIndicatorMessageVisible: Flow<Boolean> = @@ -160,7 +171,9 @@ constructor( /** If the icon can be used as a confirmation button. */ val isIconConfirmButton: Flow<Boolean> = - combine(size, interactor.isConfirmationRequired) { size, isConfirmationRequired -> + combine(size, promptSelectorInteractor.isConfirmationRequired) { + size, + isConfirmationRequired -> size.isNotSmall && isConfirmationRequired } @@ -169,7 +182,7 @@ constructor( combine( size, isAuthenticated, - interactor.isCredentialAllowed, + promptSelectorInteractor.isCredentialAllowed, ) { size, authState, credentialAllowed -> size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed } @@ -221,7 +234,7 @@ constructor( combine( size, isAuthenticated, - interactor.isCredentialAllowed, + promptSelectorInteractor.isCredentialAllowed, ) { size, authState, credentialAllowed -> size.isNotSmall && authState.isNotAuthenticated && credentialAllowed } diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt index 323070a84863..03d787bc6c8f 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt @@ -89,8 +89,9 @@ class LongPressHandlingView( } @SuppressLint("ClickableViewAccessibility") - override fun onTouchEvent(event: MotionEvent?): Boolean { - return interactionHandler.onTouchEvent(event?.toModel()) + override fun onTouchEvent(event: MotionEvent): Boolean { + super.onTouchEvent(event) + return interactionHandler.onTouchEvent(event.toModel()) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt index b0e3132a1fc7..09383842a31e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt @@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import com.android.systemui.R -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 9b323ee9a3f3..9e63be5d0042 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -24,6 +24,8 @@ import com.android.keyguard.KeyguardStatusViewController import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable import com.android.systemui.R +import com.android.systemui.animation.view.LaunchableLinearLayout +import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel @@ -34,6 +36,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteract import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder +import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder @@ -42,6 +45,7 @@ import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandList import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel @@ -80,6 +84,7 @@ constructor( private val falsingManager: FalsingManager, private val vibratorHelper: VibratorHelper, private val keyguardStateController: KeyguardStateController, + private val keyguardLongPressViewModel: KeyguardLongPressViewModel, private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel, private val activityStarter: ActivityStarter, private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, @@ -112,7 +117,7 @@ constructor( bindLeftShortcut() bindRightShortcut() bindAmbientIndicationArea() - bindSettingsPopupMenu() + bindSettingsPopupMenu(notificationPanel) bindCommunalWidgetArea() KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) @@ -202,12 +207,34 @@ constructor( } } - private fun bindSettingsPopupMenu() { + private fun bindSettingsPopupMenu(legacyParent: ViewGroup) { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + // Remove the legacy long-press view from the NotificationPanelView where it used to be + // before this refactor such that we only have one long-press view at the bottom of + // KeyguardRootView. + val legacyLongPressView = legacyParent.requireViewById<View>(R.id.keyguard_long_press) + legacyParent.removeView(legacyLongPressView) + + val longPressView: LongPressHandlingView = + keyguardRootView.requireViewById(R.id.keyguard_long_press) + val settingsMenuView: LaunchableLinearLayout = + keyguardRootView.requireViewById(R.id.keyguard_settings_button) + + // Bind the long-press view that (1) triggers the showing of the settings popup menu and + // (2) captures touch events outside of the shown settings popup menu to hide it. + KeyguardLongPressViewBinder.bind( + view = longPressView, + viewModel = keyguardLongPressViewModel, + onSingleTap = {}, + falsingManager = falsingManager, + settingsMenuView = settingsMenuView, + ) + + // Bind the settings popup menu. settingsPopupMenuHandle?.dispose() settingsPopupMenuHandle = KeyguardSettingsViewBinder.bind( - keyguardRootView, + settingsMenuView, keyguardSettingsMenuViewModel, vibratorHelper, activityStarter, @@ -216,6 +243,9 @@ constructor( keyguardRootView.findViewById<View?>(R.id.keyguard_settings_button)?.let { keyguardRootView.removeView(it) } + keyguardRootView.findViewById<View?>(R.id.keyguard_long_press)?.let { + keyguardRootView.removeView(it) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt index 7234757081e2..2fb40a4f1b51 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt @@ -110,11 +110,3 @@ interface KeyguardBlueprint { .forEach { constraintSet.setVisibility(it, View.GONE) } } } - -/** - * Lower level modules that determine constraints for a particular section in the lockscreen root - * view. - */ -interface KeyguardSection { - fun apply(constraintSet: ConstraintSet) -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt new file mode 100644 index 000000000000..405d75e3de7d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt @@ -0,0 +1,27 @@ +/* + * Copyright 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.keyguard.shared.model + +import androidx.constraintlayout.widget.ConstraintSet + +/** + * Lower level modules that determine constraints for a particular section in the lockscreen root + * view. + */ +interface KeyguardSection { + fun apply(constraintSet: ConstraintSet) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 44acf4f0fd2d..1669f4460461 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.content.Intent -import android.graphics.Rect import android.graphics.drawable.Animatable2 import android.util.Size import android.view.View @@ -76,7 +75,7 @@ object KeyguardBottomAreaViewBinder { * Users of the [KeyguardBottomAreaViewBinder] class should use this to control the binder after * it is bound. */ - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] @Deprecated("Deprecated as part of b/278057014") interface Binding { /** @@ -119,17 +118,6 @@ object KeyguardBottomAreaViewBinder { view.clipChildren = false view.clipToPadding = false - view.setOnTouchListener { _, event -> - if (settingsMenu.isVisible) { - val hitRect = Rect() - settingsMenu.getHitRect(hitRect) - if (!hitRect.contains(event.x.toInt(), event.y.toInt())) { - viewModel.onTouchedOutsideLockScreenSettingsMenu() - } - } - - false - } val configurationBasedDimensions = MutableStateFlow(loadFromResources(view)) @@ -137,7 +125,7 @@ object KeyguardBottomAreaViewBinder { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] launch { viewModel.startButton.collect { buttonModel -> updateButton( @@ -150,7 +138,7 @@ object KeyguardBottomAreaViewBinder { } } - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] launch { viewModel.endButton.collect { buttonModel -> updateButton( @@ -188,7 +176,7 @@ object KeyguardBottomAreaViewBinder { } } - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] launch { updateButtonAlpha( view = startButton, @@ -197,7 +185,7 @@ object KeyguardBottomAreaViewBinder { ) } - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] launch { updateButtonAlpha( view = endButton, @@ -223,7 +211,7 @@ object KeyguardBottomAreaViewBinder { } } - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] launch { configurationBasedDimensions.collect { dimensions -> startButton.updateLayoutParams<ViewGroup.LayoutParams> { @@ -385,13 +373,14 @@ object KeyguardBottomAreaViewBinder { view.isClickable = viewModel.isClickable if (viewModel.isClickable) { if (viewModel.useLongPress) { - val onTouchListener = KeyguardQuickAffordanceOnTouchListener( - view, - viewModel, - messageDisplayer, - vibratorHelper, - falsingManager, - ) + val onTouchListener = + KeyguardQuickAffordanceOnTouchListener( + view, + viewModel, + messageDisplayer, + vibratorHelper, + falsingManager, + ) view.setOnTouchListener(onTouchListener) view.onLongClickListener = OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener) @@ -408,7 +397,7 @@ object KeyguardBottomAreaViewBinder { } @Deprecated("Deprecated as part of b/278057014") - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] private suspend fun updateButtonAlpha( view: View, viewModel: Flow<KeyguardQuickAffordanceViewModel>, @@ -439,7 +428,7 @@ object KeyguardBottomAreaViewBinder { } @Deprecated("Deprecated as part of b/278057014") - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] private class OnLongClickListener( private val falsingManager: FalsingManager?, private val viewModel: KeyguardQuickAffordanceViewModel, @@ -476,7 +465,7 @@ object KeyguardBottomAreaViewBinder { } @Deprecated("Deprecated as part of b/278057014") - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] private class OnClickListener( private val viewModel: KeyguardQuickAffordanceViewModel, private val falsingManager: FalsingManager, @@ -532,7 +521,7 @@ object KeyguardBottomAreaViewBinder { } @Deprecated("Deprecated as part of b/278057014") - //If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] + // If updated, be sure to update [KeyguardQuickAffordanceViewBinder.kt] private data class ConfigurationBasedDimensions( val defaultBurnInPreventionYOffsetPx: Int, val buttonSizePx: Size, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index 9cc503c07955..48fd24acdb34 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -17,7 +17,11 @@ package com.android.systemui.keyguard.ui.binder +import android.annotation.SuppressLint +import android.graphics.Rect +import android.view.MotionEvent import android.view.View +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.common.ui.view.LongPressHandlingView @@ -35,6 +39,9 @@ object KeyguardLongPressViewBinder { * @param onSingleTap A callback to invoke when the system decides that there was a single tap. * @param falsingManager [FalsingManager] for making sure the long-press didn't just happen in * the user's pocket. + * @param settingsMenuView The [View] for the settings menu that shows up when the long-press is + * detected. The [view] will be monitored for all touch to know when to actually hide the + * settings menu after it had been shown due to an outside touch. */ @JvmStatic fun bind( @@ -42,6 +49,7 @@ object KeyguardLongPressViewBinder { viewModel: KeyguardLongPressViewModel, onSingleTap: () -> Unit, falsingManager: FalsingManager, + settingsMenuView: View, ) { view.listener = object : LongPressHandlingView.Listener { @@ -62,6 +70,12 @@ object KeyguardLongPressViewBinder { } } + listenForTouchOutsideDismissals( + outsideView = view, + settingsMenuView = settingsMenuView, + onTouchedOutsideSettingsMenu = viewModel::onTouchedOutside, + ) + view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -72,4 +86,27 @@ object KeyguardLongPressViewBinder { } } } + + /** Listens for and handles touches outside the settings menu view, to dismiss it. */ + @SuppressLint("ClickableViewAccessibility") + private fun listenForTouchOutsideDismissals( + outsideView: View, + settingsMenuView: View, + onTouchedOutsideSettingsMenu: () -> Unit, + ) { + outsideView.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN && settingsMenuView.isVisible) { + val hitRect = Rect() + settingsMenuView.getHitRect(hitRect) + if (!hitRect.contains(event.x.toInt(), event.y.toInt())) { + onTouchedOutsideSettingsMenu() + true + } else { + false + } + } else { + false + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt index 82610e6ea59d..95569f6ad4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt @@ -38,13 +38,11 @@ import kotlinx.coroutines.launch object KeyguardSettingsViewBinder { fun bind( - parentView: View, + view: LaunchableLinearLayout, viewModel: KeyguardSettingsMenuViewModel, vibratorHelper: VibratorHelper, activityStarter: ActivityStarter ): DisposableHandle { - val view = parentView.requireViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button) - val disposableHandle = view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -127,5 +125,4 @@ object KeyguardSettingsViewBinder { } .start() } - -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index a94874176a34..a69a9d5d07b5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.view import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.widget.ImageView import androidx.constraintlayout.widget.ConstraintLayout @@ -28,6 +29,7 @@ import com.android.keyguard.KeyguardStatusView import com.android.keyguard.LockIconView import com.android.systemui.R import com.android.systemui.animation.view.LaunchableImageView +import com.android.systemui.common.ui.view.LongPressHandlingView /** Provides a container for all keyguard ui content. */ class KeyguardRootView( @@ -39,9 +41,11 @@ class KeyguardRootView( attrs, ) { + var motionEventSpy: ((MotionEvent) -> Unit)? = null private var statusView: KeyguardStatusView? = null init { + addLongPressHandlingView() addIndicationTextArea() addLockIconView() addAmbientIndicationArea() @@ -51,6 +55,15 @@ class KeyguardRootView( addStatusView() } + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + motionEventSpy?.invoke(event) + return super.onInterceptTouchEvent(event) + } + + private fun addLongPressHandlingView() { + addView(LongPressHandlingView(context, attrs).apply { id = R.id.keyguard_long_press }) + } + private fun addIndicationTextArea() { val view = KeyguardIndicationArea(context, attrs) addView(view) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index 518df0719aaa..5ae30c1afc31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -23,6 +23,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLongPressHandlingSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection @@ -43,6 +44,7 @@ constructor( private val defaultLockIconSection: DefaultLockIconSection, private val defaultShortcutsSection: DefaultShortcutsSection, private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection, + private val defaultLongPressHandlingSection: DefaultLongPressHandlingSection, private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, private val defaultStatusViewSection: DefaultStatusViewSection, private val splitShadeGuidelines: SplitShadeGuidelines, @@ -54,6 +56,7 @@ constructor( defaultLockIconSection.apply(constraintSet) defaultShortcutsSection.apply(constraintSet) defaultAmbientIndicationAreaSection.apply(constraintSet) + defaultLongPressHandlingSection.apply(constraintSet) defaultSettingsPopupMenuSection.apply(constraintSet) defaultStatusViewSection.apply(constraintSet) splitShadeGuidelines.apply(constraintSet) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index 54c27960db3c..7a1f030fa70a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdf import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLongPressHandlingSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection @@ -40,6 +41,7 @@ constructor( private val defaultIndicationAreaSection: DefaultIndicationAreaSection, private val defaultLockIconSection: DefaultLockIconSection, private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection, + private val defaultLongPressHandlingSection: DefaultLongPressHandlingSection, private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection, private val defaultShortcutsSection: DefaultShortcutsSection, @@ -52,6 +54,7 @@ constructor( defaultIndicationAreaSection.apply(constraintSet) defaultLockIconSection.apply(constraintSet) defaultAmbientIndicationAreaSection.apply(constraintSet) + defaultLongPressHandlingSection.apply(constraintSet) defaultSettingsPopupMenuSection.apply(constraintSet) if (keyguardUpdateMonitor.isUdfpsSupported) { alignShortcutsToUdfpsSection.apply(constraintSet) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt index 156b9f3e5f48..f7c27ffd936c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt @@ -26,7 +26,7 @@ import androidx.constraintlayout.widget.ConstraintSet.RIGHT import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class AlignShortcutsToUdfpsSection @Inject constructor(@Main private val resources: Resources) : diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt index abf25a23439f..937674e51421 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt @@ -28,7 +28,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultAmbientIndicationAreaSection diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt index dee7ed570b05..93c1ef70466d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt @@ -21,7 +21,7 @@ import android.content.Context import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.R -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultIndicationAreaSection @Inject constructor(private val context: Context) : diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt index 461faec217ca..f69f9c71141c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt @@ -27,7 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.biometrics.AuthController -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultLockIconSection diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLongPressHandlingSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLongPressHandlingSection.kt new file mode 100644 index 000000000000..5470b4119b96 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLongPressHandlingSection.kt @@ -0,0 +1,47 @@ +/* + * Copyright 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.keyguard.ui.view.layout.sections + +import androidx.annotation.IdRes +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.R +import com.android.systemui.keyguard.shared.model.KeyguardSection +import javax.inject.Inject + +/** Positions the long-press handling view in the keyguard. */ +class DefaultLongPressHandlingSection @Inject constructor() : KeyguardSection { + override fun apply(constraintSet: ConstraintSet) { + constraintSet.fillMaxSize(R.id.keyguard_long_press) + } + + private fun ConstraintSet.fillMaxSize(@IdRes viewId: Int) { + listOf( + ConstraintSet.START, + ConstraintSet.TOP, + ConstraintSet.END, + ConstraintSet.BOTTOM, + ) + .forEach { side -> + connect( + viewId, + side, + ConstraintSet.PARENT_ID, + side, + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt index ad1e4f8b98b4..631d20db95dd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt @@ -26,7 +26,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val resources: Resources) : diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt index db4653defd34..c349664c94af 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt @@ -25,7 +25,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject class DefaultShortcutsSection @Inject constructor(@Main private val resources: Resources) : diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt index 3f319ba2d0e4..5e832f6cc89c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -18,20 +18,18 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context -import android.view.ViewGroup -import androidx.constraintlayout.widget.ConstraintSet -import com.android.systemui.R -import com.android.systemui.keyguard.data.repository.KeyguardSection -import javax.inject.Inject import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP -import androidx.constraintlayout.widget.ConstraintSet.END +import com.android.systemui.R +import com.android.systemui.keyguard.shared.model.KeyguardSection +import javax.inject.Inject -class DefaultStatusViewSection @Inject constructor(private val context: Context) : - KeyguardSection { +class DefaultStatusViewSection @Inject constructor(private val context: Context) : KeyguardSection { private val statusViewId = R.id.keyguard_status_view override fun apply(constraintSet: ConstraintSet) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt index 668b17ffeba0..92e37b75e53a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt @@ -18,21 +18,13 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context -import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.VERTICAL import com.android.systemui.R -import com.android.systemui.keyguard.data.repository.KeyguardSection +import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT -import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT -import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID -import androidx.constraintlayout.widget.ConstraintSet.START -import androidx.constraintlayout.widget.ConstraintSet.TOP -import androidx.constraintlayout.widget.ConstraintSet.END -import androidx.constraintlayout.widget.ConstraintSet.VERTICAL -class SplitShadeGuidelines @Inject constructor(private val context: Context) : - KeyguardSection { +class SplitShadeGuidelines @Inject constructor(private val context: Context) : KeyguardSection { override fun apply(constraintSet: ConstraintSet) { constraintSet.apply { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt index 3e6f8e68891a..74eb85640341 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt @@ -157,14 +157,6 @@ constructor( selectedPreviewSlotId.value = slotId } - /** - * Notifies that some input gesture has started somewhere in the bottom area that's outside of - * the lock screen settings menu item pop-up. - */ - fun onTouchedOutsideLockScreenSettingsMenu() { - longPressViewModel.onTouchedOutside() - } - private fun button( position: KeyguardQuickAffordancePosition ): Flow<KeyguardQuickAffordanceViewModel> { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 014093de62bd..32a5fdfb3e7b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -546,6 +546,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final NPVCDownEventState.Buffer mLastDownEvents; private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; + private final KeyguardLongPressViewModel mKeyguardLongPressViewModel; private float mMinExpandHeight; private boolean mPanelUpdateWhenAnimatorEnds; private boolean mHasVibratedOnOpen = false; @@ -949,14 +950,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor; - KeyguardLongPressViewBinder.bind( - mView.requireViewById(R.id.keyguard_long_press), - keyguardLongPressViewModel, - () -> { - onEmptySpaceClick(); - return Unit.INSTANCE; - }, - mFalsingManager); + mKeyguardLongPressViewModel = keyguardLongPressViewModel; mActivityStarter = activityStarter; onFinishInflate(); keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( @@ -1500,6 +1494,16 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Deprecated private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) { mKeyguardBottomArea = keyguardBottomArea; + + KeyguardLongPressViewBinder.bind( + mView.requireViewById(R.id.keyguard_long_press), + mKeyguardLongPressViewModel, + () -> { + onEmptySpaceClick(); + return Unit.INSTANCE; + }, + mFalsingManager, + mKeyguardBottomArea.requireViewById(R.id.keyguard_settings_button)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index aea3030967d2..9dca013f8aa4 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -109,7 +109,6 @@ public class ImageWallpaper extends WallpaperService { private WallpaperManager mWallpaperManager; private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor; private SurfaceHolder mSurfaceHolder; - private boolean mDrawn = false; @VisibleForTesting static final int MIN_SURFACE_WIDTH = 128; @VisibleForTesting @@ -240,7 +239,6 @@ public class ImageWallpaper extends WallpaperService { private void drawFrameSynchronized() { synchronized (mLock) { - if (mDrawn) return; drawFrameInternal(); } } @@ -278,7 +276,6 @@ public class ImageWallpaper extends WallpaperService { Rect dest = mSurfaceHolder.getSurfaceFrame(); try { canvas.drawBitmap(bitmap, null, dest, null); - mDrawn = true; } finally { surface.unlockCanvasAndPost(canvas); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 4e52e64a8af1..9584d888b01f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -39,13 +39,13 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.widget.LockPatternUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel import com.android.systemui.flags.FakeFeatureFlags @@ -109,6 +109,7 @@ open class AuthContainerViewTest : SysuiTestCase() { private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val biometricPromptRepository = FakePromptRepository() + private val fingerprintRepository = FakeFingerprintPropertyRepository() private val rearDisplayStateRepository = FakeRearDisplayStateRepository() private val credentialInteractor = FakeCredentialInteractor() private val bpCredentialInteractor = PromptCredentialInteractor( @@ -118,10 +119,12 @@ open class AuthContainerViewTest : SysuiTestCase() { ) private val promptSelectorInteractor by lazy { PromptSelectorInteractorImpl( + fingerprintRepository, biometricPromptRepository, lockPatternUtils, ) } + private val displayStateInteractor = DisplayStateInteractorImpl( testScope.backgroundScope, mContext, @@ -129,9 +132,7 @@ open class AuthContainerViewTest : SysuiTestCase() { rearDisplayStateRepository ) - private val authBiometricFingerprintViewModel = AuthBiometricFingerprintViewModel( - displayStateInteractor - ) + private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) private var authContainer: TestAuthContainerView? = null @@ -524,10 +525,14 @@ open class AuthContainerViewTest : SysuiTestCase() { userManager, lockPatternUtils, interactionJankMonitor, - { authBiometricFingerprintViewModel }, { promptSelectorInteractor }, { bpCredentialInteractor }, - PromptViewModel(promptSelectorInteractor, vibrator, featureFlags), + PromptViewModel( + displayStateInteractor, + promptSelectorInteractor, + vibrator, + featureFlags + ), { credentialViewModel }, Handler(TestableLooper.get(this).looper), fakeExecutor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 48e513140c3a..6d71dd5cd8ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -92,7 +92,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; -import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.flags.FakeFeatureFlags; @@ -177,8 +176,6 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private PromptSelectorInteractor mPromptSelectionInteractor; @Mock - private AuthBiometricFingerprintViewModel mAuthBiometricFingerprintViewModel; - @Mock private CredentialViewModel mCredentialViewModel; @Mock private PromptViewModel mPromptViewModel; @@ -1095,11 +1092,10 @@ public class AuthControllerTest extends SysuiTestCase { mFingerprintManager, mFaceManager, () -> mUdfpsController, () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle, mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger, - mLogContextInteractor, () -> mAuthBiometricFingerprintViewModel, - () -> mBiometricPromptCredentialInteractor, () -> mPromptSelectionInteractor, - () -> mCredentialViewModel, () -> mPromptViewModel, - mInteractionJankMonitor, mHandler, - mBackgroundExecutor, mUdfpsUtils, mVibratorHelper); + mLogContextInteractor, () -> mBiometricPromptCredentialInteractor, + () -> mPromptSelectionInteractor, () -> mCredentialViewModel, + () -> mPromptViewModel, mInteractionJankMonitor, mHandler, mBackgroundExecutor, + mUdfpsUtils, mVibratorHelper); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index 81cbaeab2a32..4d5e1b7de60f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.Utils +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.domain.model.BiometricModalities import com.android.systemui.biometrics.faceSensorPropertiesInternal @@ -61,13 +62,15 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils private val testScope = TestScope() + private val fingerprintRepository = FakeFingerprintPropertyRepository() private val promptRepository = FakePromptRepository() private lateinit var interactor: PromptSelectorInteractor @Before fun setup() { - interactor = PromptSelectorInteractorImpl(promptRepository, lockPatternUtils) + interactor = + PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt index 785f1be89986..7697c098ed3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt @@ -2,10 +2,17 @@ package com.android.systemui.biometrics.ui.viewmodel import android.content.res.Configuration import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -16,39 +23,52 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) -class AuthBiometricFingerprintViewModelTest : SysuiTestCase() { +class PromptFingerprintIconViewModelTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var lockPatternUtils: LockPatternUtils + + private val fingerprintRepository = FakeFingerprintPropertyRepository() + private val promptRepository = FakePromptRepository() private val rearDisplayStateRepository = FakeRearDisplayStateRepository() + private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) - private lateinit var interactor: DisplayStateInteractor - private lateinit var viewModel: AuthBiometricFingerprintViewModel + private lateinit var promptSelectorInteractor: PromptSelectorInteractor + private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var viewModel: PromptFingerprintIconViewModel @Before fun setup() { - interactor = + promptSelectorInteractor = + PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) + displayStateInteractor = DisplayStateInteractorImpl( testScope.backgroundScope, mContext, fakeExecutor, rearDisplayStateRepository ) - viewModel = AuthBiometricFingerprintViewModel(interactor) + viewModel = PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor) } @Test - fun iconUpdates_onConfigurationChanged() { + fun sfpsIconUpdates_onConfigurationChanged() { testScope.runTest { runCurrent() - + configureFingerprintPropertyRepository(FingerprintSensorType.POWER_BUTTON) val testConfig = Configuration() val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1 val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1 @@ -65,6 +85,10 @@ class AuthBiometricFingerprintViewModelTest : SysuiTestCase() { assertThat(foldedIcon).isNotEqualTo(unfoldedIcon) } } + + private fun configureFingerprintPropertyRepository(sensorType: FingerprintSensorType) { + fingerprintRepository.setProperties(0, SensorStrength.STRONG, sensorType, mapOf()) + } } internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 4d19543d41ff..11b0b0798ebc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -24,7 +24,10 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthBiometricView +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository +import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl import com.android.systemui.biometrics.domain.model.BiometricModalities @@ -37,7 +40,9 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -69,8 +74,19 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var vibrator: VibratorHelper + private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val testScope = TestScope() + private val fingerprintRepository = FakeFingerprintPropertyRepository() private val promptRepository = FakePromptRepository() + private val rearDisplayStateRepository = FakeRearDisplayStateRepository() + + private val displayStateInteractor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + rearDisplayStateRepository + ) private lateinit var selector: PromptSelectorInteractor private lateinit var viewModel: PromptViewModel @@ -78,10 +94,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Before fun setup() { - selector = PromptSelectorInteractorImpl(promptRepository, lockPatternUtils) + selector = + PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) selector.resetPrompt() - viewModel = PromptViewModel(selector, vibrator, featureFlags) + viewModel = PromptViewModel(displayStateInteractor, selector, vibrator, featureFlags) featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) } @@ -105,7 +122,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } assertThat(message).isEqualTo(PromptMessage.Empty) assertThat(size).isEqualTo(expectedSize) - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN) + assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_IDLE) val startMessage = "here we go" viewModel.showAuthenticating(startMessage, isRetry = false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index addb1815cead..d00fe9d309b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLongPressHandlingSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection @@ -48,6 +49,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { @Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection @Mock private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection + @Mock private lateinit var defaultLongPressHandlingSection: DefaultLongPressHandlingSection @Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection @Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection @Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines @@ -62,6 +64,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { defaultLockIconSection, defaultShortcutsSection, defaultAmbientIndicationAreaSection, + defaultLongPressHandlingSection, defaultSettingsPopupMenuSection, defaultStatusViewSection, splitShadeGuidelines, diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 1536c1737de6..c4a80bff29d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -69,7 +69,6 @@ import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles import com.google.common.truth.Truth.assertThat import java.util.Optional -import kotlin.test.assertNotNull import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -673,7 +672,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { extras().bool(EXTRA_USE_STYLUS_MODE).isTrue() } iconCaptor.value?.let { icon -> - assertNotNull(icon) + assertThat(icon).isNotNull() assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 981e44bea846..270fa7a5c85d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -80,6 +80,7 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.view.LaunchableLinearLayout; import com.android.systemui.biometrics.AuthController; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.classifier.FalsingCollectorFake; @@ -411,6 +412,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); when(mKeyguardBottomArea.animate()).thenReturn(mViewPropertyAnimator); + when(mKeyguardBottomArea.requireViewById(R.id.keyguard_settings_button)) + .thenReturn(mock(LaunchableLinearLayout.class)); when(mView.animate()).thenReturn(mViewPropertyAnimator); when(mKeyguardStatusView.animate()).thenReturn(mViewPropertyAnimator); when(mViewPropertyAnimator.translationX(anyFloat())).thenReturn(mViewPropertyAnimator); diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java new file mode 100644 index 000000000000..a3efb25a5735 --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java @@ -0,0 +1,55 @@ +/* + * 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.server.autofill; + +import android.util.Slog; +import android.view.inputmethod.InlineSuggestionsRequest; + +import java.lang.ref.WeakReference; +import java.util.function.Consumer; + +class InlineSuggestionRequestConsumer implements Consumer<InlineSuggestionsRequest> { + + static final String TAG = "InlineSuggestionRequestConsumer"; + + private final WeakReference<Session.AssistDataReceiverImpl> mAssistDataReceiverWeakReference; + private final WeakReference<ViewState> mViewStateWeakReference; + + InlineSuggestionRequestConsumer(WeakReference<Session.AssistDataReceiverImpl> + assistDataReceiverWeakReference, + WeakReference<ViewState> viewStateWeakReference) { + mAssistDataReceiverWeakReference = assistDataReceiverWeakReference; + mViewStateWeakReference = viewStateWeakReference; + } + + @Override + public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) { + Session.AssistDataReceiverImpl assistDataReceiver = mAssistDataReceiverWeakReference.get(); + ViewState viewState = mViewStateWeakReference.get(); + if (assistDataReceiver == null) { + Slog.wtf(TAG, "assistDataReceiver is null when accepting new inline suggestion" + + "requests"); + return; + } + + if (viewState == null) { + Slog.wtf(TAG, "view state is null when accepting new inline suggestion requests"); + return; + } + assistDataReceiver.handleInlineSuggestionRequest(inlineSuggestionsRequest, viewState); + } +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0c4f8771ef02..1ae912544cc8 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -326,7 +326,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Id of the View currently being displayed. */ @GuardedBy("mLock") - @Nullable AutofillId mCurrentViewId; + private @Nullable AutofillId mCurrentViewId; @GuardedBy("mLock") private IAutoFillManagerClient mClient; @@ -623,7 +623,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * TODO(b/151867668): improve how asynchronous data dependencies are handled, without using * CountDownLatch. */ - private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { + final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") private boolean mWaitForInlineRequest; @GuardedBy("mLock") @@ -638,18 +638,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPendingFillRequest = null; mWaitForInlineRequest = isInlineRequest; mPendingInlineSuggestionsRequest = null; - return isInlineRequest ? (inlineSuggestionsRequest) -> { - synchronized (mLock) { - if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) { - return; - } - mWaitForInlineRequest = inlineSuggestionsRequest != null; - mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; - mWaitForInlineRequest = inlineSuggestionsRequest != null; - maybeRequestFillFromServiceLocked(); - viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); + if (isInlineRequest) { + WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference = + new WeakReference<AssistDataReceiverImpl>(this); + WeakReference<ViewState> viewStateWeakReference = + new WeakReference<ViewState>(viewState); + return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference, + viewStateWeakReference); + } + return null; + } + + void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest, + ViewState viewState) { + synchronized (mLock) { + if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) { + return; } - } : null; + mWaitForInlineRequest = inlineSuggestionsRequest != null; + mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; + maybeRequestFillFromServiceLocked(); + viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); + } } void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index fd45d2423227..69647633eaff 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -313,14 +313,14 @@ class AssociationRequestsProcessor { public void enableSystemDataSync(int associationId, int flags) { AssociationInfo association = mAssociationStore.getAssociationById(associationId); - AssociationInfo updated = AssociationInfo.builder(association) + AssociationInfo updated = (new AssociationInfo.Builder(association)) .setSystemDataSyncFlags(association.getSystemDataSyncFlags() | flags).build(); mAssociationStore.updateAssociation(updated); } public void disableSystemDataSync(int associationId, int flags) { AssociationInfo association = mAssociationStore.getAssociationById(associationId); - AssociationInfo updated = AssociationInfo.builder(association) + AssociationInfo updated = (new AssociationInfo.Builder(association)) .setSystemDataSyncFlags(association.getSystemDataSyncFlags() & (~flags)).build(); mAssociationStore.updateAssociation(updated); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 996c68b5bf83..1ce7d9691fb5 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -891,7 +891,7 @@ public class CompanionDeviceManagerService extends SystemService { } // AssociationInfo class is immutable: create a new AssociationInfo object with updated // timestamp. - association = AssociationInfo.builder(association) + association = (new AssociationInfo.Builder(association)) .setLastTimeConnected(System.currentTimeMillis()) .build(); mAssociationStore.updateAssociation(association); @@ -945,7 +945,7 @@ public class CompanionDeviceManagerService extends SystemService { // AssociationInfo class is immutable: create a new AssociationInfo object with updated // flag. - association = AssociationInfo.builder(association) + association = (new AssociationInfo.Builder(association)) .setNotifyOnDeviceNearby(active) .build(); // Do not need to call {@link BleCompanionDeviceScanner#restartScan()} since it will @@ -1016,7 +1016,7 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void setAssociationTag(int associationId, String tag) { AssociationInfo association = getAssociationWithCallerChecks(associationId); - association = AssociationInfo.builder(association).setTag(tag).build(); + association = (new AssociationInfo.Builder(association)).setTag(tag).build(); mAssociationStore.updateAssociation(association); } @@ -1255,7 +1255,7 @@ public class CompanionDeviceManagerService extends SystemService { */ private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) { // First: set revoked flag. - association = AssociationInfo.builder(association) + association = (new AssociationInfo.Builder(association)) .setRevoked(true) .build(); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index f4f5c951faaa..d256aead97e8 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -580,7 +580,6 @@ public class PackageWatchdog { PackageHealthObserverImpact.USER_IMPACT_LEVEL_10, PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, - PackageHealthObserverImpact.USER_IMPACT_LEVEL_60, PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) public @interface PackageHealthObserverImpact { @@ -591,7 +590,6 @@ public class PackageWatchdog { /* Actions having medium user impact, user of a device will likely notice. */ int USER_IMPACT_LEVEL_30 = 30; int USER_IMPACT_LEVEL_50 = 50; - int USER_IMPACT_LEVEL_60 = 60; int USER_IMPACT_LEVEL_70 = 70; /* Action has high user impact, a last resort, user of a device will be very frustrated. */ int USER_IMPACT_LEVEL_100 = 100; diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java index 2b21e49a4e03..67c221f77037 100644 --- a/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java +++ b/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input.debug; +package com.android.server.input; import android.view.Display; import android.view.InputEvent; @@ -22,7 +22,6 @@ import android.view.InputEventReceiver; import android.view.MotionEvent; import com.android.server.UiThread; -import com.android.server.input.InputManagerService; /** * Receives input events before they are dispatched and reports them to FocusEventDebugView. diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java index 6eec0dee9152..4b8fabde7d35 100644 --- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java +++ b/services/core/java/com/android/server/input/FocusEventDebugView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input.debug; +package com.android.server.input; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.util.TypedValue.COMPLEX_UNIT_SP; @@ -24,9 +24,11 @@ import android.animation.LayoutTransition; import android.annotation.AnyThread; import android.annotation.Nullable; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; import android.graphics.Typeface; import android.util.DisplayMetrics; import android.util.Pair; @@ -38,6 +40,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RoundedCorner; import android.view.View; +import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.widget.HorizontalScrollView; @@ -47,17 +50,19 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.input.InputManagerService; import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on * the screen. */ -public class FocusEventDebugView extends RelativeLayout { +class FocusEventDebugView extends RelativeLayout { private static final String TAG = FocusEventDebugView.class.getSimpleName(); @@ -107,7 +112,7 @@ public class FocusEventDebugView extends RelativeLayout { mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm); } - public FocusEventDebugView(Context c, InputManagerService service) { + FocusEventDebugView(Context c, InputManagerService service) { this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c)); } @@ -144,13 +149,11 @@ public class FocusEventDebugView extends RelativeLayout { return super.dispatchKeyEvent(event); } - /** Determines whether to show the key presses visualization. */ @AnyThread public void updateShowKeyPresses(boolean enabled) { post(() -> handleUpdateShowKeyPresses(enabled)); } - /** Determines whether to show the rotary input visualization. */ @AnyThread public void updateShowRotaryInput(boolean enabled) { post(() -> handleUpdateShowRotaryInput(enabled)); @@ -355,6 +358,13 @@ public class FocusEventDebugView extends RelativeLayout { return mRotaryInputValueView != null; } + /** + * Converts a dimension in scaled pixel units to integer display pixels. + */ + private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { + return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); + } + private static class PressedKeyView extends TextView { private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{ @@ -463,4 +473,376 @@ public class FocusEventDebugView extends RelativeLayout { invalidate(); } } + + // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage. + + /** Draws the most recent rotary input value and indicates whether the source is active. */ + @VisibleForTesting + static class RotaryInputValueView extends TextView { + + private static final int INACTIVE_TEXT_COLOR = 0xffff00ff; + private static final int ACTIVE_TEXT_COLOR = 0xff420f28; + private static final int TEXT_SIZE_SP = 8; + private static final int SIDE_PADDING_SP = 4; + /** Determines how long the active status lasts. */ + private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */; + private static final ColorFilter ACTIVE_BACKGROUND_FILTER = + new ColorMatrixColorFilter(new float[]{ + 0, 0, 0, 0, 255, // red + 0, 0, 0, 0, 0, // green + 0, 0, 0, 0, 255, // blue + 0, 0, 0, 0, 200 // alpha + }); + + private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false); + private final float mScaledVerticalScrollFactor; + + @VisibleForTesting + RotaryInputValueView(Context c) { + super(c); + + DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + + setText(getFormattedValue(0)); + setTextColor(INACTIVE_TEXT_COLOR); + setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm)); + setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0, + applyDimensionSp(SIDE_PADDING_SP, dm), 0); + setTypeface(null, Typeface.BOLD); + setBackgroundResource(R.drawable.focus_event_rotary_input_background); + } + + void updateValue(float value) { + removeCallbacks(mUpdateActivityStatusCallback); + + setText(getFormattedValue(value * mScaledVerticalScrollFactor)); + + updateActivityStatus(true); + postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); + } + + @VisibleForTesting + void updateActivityStatus(boolean active) { + if (active) { + setTextColor(ACTIVE_TEXT_COLOR); + getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); + } else { + setTextColor(INACTIVE_TEXT_COLOR); + getBackground().clearColorFilter(); + } + } + + private static String getFormattedValue(float value) { + return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); + } + } + + /** + * Shows a graph with the rotary input values as a function of time. + * The graph gets reset if no action is received for a certain amount of time. + */ + @VisibleForTesting + static class RotaryInputGraphView extends View { + + private static final int FRAME_COLOR = 0xbf741b47; + private static final int FRAME_WIDTH_SP = 2; + private static final int FRAME_BORDER_GAP_SP = 10; + private static final int FRAME_TEXT_SIZE_SP = 10; + private static final int FRAME_TEXT_OFFSET_SP = 2; + private static final int GRAPH_COLOR = 0xffff00ff; + private static final int GRAPH_LINE_WIDTH_SP = 1; + private static final int GRAPH_POINT_RADIUS_SP = 4; + private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5); + private static final float DEFAULT_FRAME_CENTER_POSITION = 0; + private static final int MAX_GRAPH_VALUES_SIZE = 400; + /** Maximum time between values so that they are considered part of the same gesture. */ + private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1); + + private final DisplayMetrics mDm; + /** + * Distance in position units (amount scrolled in display pixels) from the center to the + * top/bottom frame lines. + */ + private final float mFrameCenterToBorderDistance; + private final float mScaledVerticalScrollFactor; + private final Locale mDefaultLocale; + private final Paint mFramePaint = new Paint(); + private final Paint mFrameTextPaint = new Paint(); + private final Paint mGraphLinePaint = new Paint(); + private final Paint mGraphPointPaint = new Paint(); + + private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE); + /** Position at which graph values are placed at the center of the graph. */ + private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + + @VisibleForTesting + RotaryInputGraphView(Context c) { + super(c); + + mDm = mContext.getResources().getDisplayMetrics(); + // This makes the center-to-border distance equivalent to the display height, meaning + // that the total height of the graph is equivalent to 2x the display height. + mFrameCenterToBorderDistance = mDm.heightPixels; + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + mDefaultLocale = Locale.getDefault(); + + mFramePaint.setColor(FRAME_COLOR); + mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm)); + + mFrameTextPaint.setColor(GRAPH_COLOR); + mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm)); + + mGraphLinePaint.setColor(GRAPH_COLOR); + mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm)); + mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND); + mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND); + + mGraphPointPaint.setColor(GRAPH_COLOR); + mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm)); + mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND); + mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND); + } + + /** + * Reads new scroll axis value and updates the list accordingly. Old positions are + * kept at the front (what you would get with getFirst), while the recent positions are + * kept at the back (what you would get with getLast). Also updates the frame center + * position to handle out-of-bounds cases. + */ + void addValue(float scrollAxisValue, long eventTime) { + // Remove values that are too old. + while (mGraphValues.getSize() > 0 + && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) { + mGraphValues.removeFirst(); + } + + // If there are no recent values, reset the frame center. + if (mGraphValues.getSize() == 0) { + mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + } + + // Handle new value. We multiply the scroll axis value by the scaled scroll factor to + // get the amount of pixels to be scrolled. We also compute the accumulated position + // by adding the current value to the last one (if not empty). + final float displacement = scrollAxisValue * mScaledVerticalScrollFactor; + final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos); + final float pos = prevPos + displacement; + + mGraphValues.add(pos, eventTime); + + // The difference between the distance of the most recent position from the center + // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center + // frame (mFrameCenterToBorderDistance). + final float verticalDiff = Math.abs(pos - mFrameCenterPosition) + - mFrameCenterToBorderDistance; + // If needed, translate frame. + if (verticalDiff > 0) { + final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1; + // Here, we update the center frame position by the exact amount needed for us to + // stay within the maximum allowed distance from the center frame. + mFrameCenterPosition += sign * verticalDiff; + } + + // Redraw canvas. + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Note: vertical coordinates in Canvas go from top to bottom, + // that is bottomY > middleY > topY. + final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm); + final int topY = verticalMargin; + final int bottomY = getHeight() - verticalMargin; + final int middleY = (topY + bottomY) / 2; + + // Note: horizontal coordinates in Canvas go from left to right, + // that is rightX > leftX. + final int leftX = 0; + final int rightX = getWidth(); + + // Draw the frame, which includes 3 lines that show the maximum, + // minimum and middle positions of the graph. + canvas.drawLine(leftX, topY, rightX, topY, mFramePaint); + canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint); + canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint); + + // Draw the position that each frame line corresponds to. + final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition + mFrameCenterToBorderDistance), + leftX, + topY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", mFrameCenterPosition), + leftX, + middleY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition - mFrameCenterToBorderDistance), + leftX, + bottomY - frameTextOffset, mFrameTextPaint + ); + + // If there are no graph values to be drawn, stop here. + if (mGraphValues.getSize() == 0) { + return; + } + + // Draw the graph using the times and positions. + // We start at the most recent value (which should be drawn at the right) and move + // to the older values (which should be drawn to the left of more recent ones). Negative + // indices are handled by circuling back to the end of the buffer. + final long mostRecentTime = mGraphValues.getLast().mTime; + float prevCoordX = 0; + float prevCoordY = 0; + float prevAge = 0; + for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) { + final GraphValue value = iter.next(); + + final int age = (int) (mostRecentTime - value.mTime); + final float pos = value.mPos; + + // We get the horizontal coordinate in time units from left to right with + // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas + // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL) + // and by multiplying it by the canvas length (rightX - leftX). Finally, we + // offset the coordinate by adding it to leftX. + final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age) + / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX); + + // We get the vertical coordinate in position units from middle to top with + // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas + // units by dividing it by half of the position-domain length + // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas + // length (middleY - topY). Finally, we offset the coordinate by subtracting + // it from middleY (we can't "add" here because the coordinate grows from top + // to bottom). + final float coordY = middleY - ((pos - mFrameCenterPosition) + / mFrameCenterToBorderDistance) * (middleY - topY); + + // Draw a point for this value. + canvas.drawPoint(coordX, coordY, mGraphPointPaint); + + // If this value is part of the same gesture as the previous one, draw a line + // between them. We ignore the first value (with age = 0). + if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) { + canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint); + } + + prevCoordX = coordX; + prevCoordY = coordY; + prevAge = age; + } + } + + @VisibleForTesting + float getFrameCenterPosition() { + return mFrameCenterPosition; + } + + /** + * Holds data needed to draw each entry in the graph. + */ + private static class GraphValue { + /** Position. */ + float mPos; + /** Time when this value was added. */ + long mTime; + + GraphValue(float pos, long time) { + this.mPos = pos; + this.mTime = time; + } + } + + /** + * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the + * old values with new ones to avoid creating new objects. + */ + private static class CyclicBuffer { + private final GraphValue[] mValues; + private final int mCapacity; + private int mSize = 0; + private int mLastIndex = 0; + + // The iteration index and counter are here to make it easier to reset them. + /** Determines the value currently pointed by the iterator. */ + private int mIteratorIndex; + /** Counts how many values have been iterated through. */ + private int mIteratorCount; + + /** Used traverse the values in reverse order. */ + private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() { + @Override + public boolean hasNext() { + return mIteratorCount <= mSize; + } + + @Override + public GraphValue next() { + // Returns the value currently pointed by the iterator and moves the iterator to + // the previous one. + mIteratorCount++; + return mValues[(mIteratorIndex-- + mCapacity) % mCapacity]; + } + }; + + CyclicBuffer(int capacity) { + mCapacity = capacity; + mValues = new GraphValue[capacity]; + } + + /** + * Add new graph value. If there is an existing object, we replace its data with the + * new one. With this, we re-use old objects instead of creating new ones. + */ + void add(float pos, long time) { + mLastIndex = (mLastIndex + 1) % mCapacity; + if (mValues[mLastIndex] == null) { + mValues[mLastIndex] = new GraphValue(pos, time); + } else { + final GraphValue oldValue = mValues[mLastIndex]; + oldValue.mPos = pos; + oldValue.mTime = time; + } + + // If needed, account for new value in the buffer size. + if (mSize != mCapacity) { + mSize++; + } + } + + int getSize() { + return mSize; + } + + GraphValue getFirst() { + final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1; + final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity; + return mValues[firstIndex]; + } + + GraphValue getLast() { + return mValues[mLastIndex]; + } + + void removeFirst() { + mSize--; + } + + /** Returns an iterator pointing at the last value. */ + Iterator<GraphValue> reverseIterator() { + mIteratorIndex = mLastIndex; + mIteratorCount = 1; + return mReverseIterator; + } + } + } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index cc972c244a45..b8e9d5dfb3bc 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -116,7 +116,6 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.input.InputManagerInternal.LidSwitchCallback; -import com.android.server.input.debug.FocusEventDebugView; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java deleted file mode 100644 index 6a9fe2ff7681..000000000000 --- a/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright 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.server.input.debug; - -import static android.util.TypedValue.COMPLEX_UNIT_SP; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewConfiguration; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Iterator; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -/** - * Shows a graph with the rotary input values as a function of time. - * The graph gets reset if no action is received for a certain amount of time. - */ -class RotaryInputGraphView extends View { - - private static final int FRAME_COLOR = 0xbf741b47; - private static final int FRAME_WIDTH_SP = 2; - private static final int FRAME_BORDER_GAP_SP = 10; - private static final int FRAME_TEXT_SIZE_SP = 10; - private static final int FRAME_TEXT_OFFSET_SP = 2; - private static final int GRAPH_COLOR = 0xffff00ff; - private static final int GRAPH_LINE_WIDTH_SP = 1; - private static final int GRAPH_POINT_RADIUS_SP = 4; - private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5); - private static final float DEFAULT_FRAME_CENTER_POSITION = 0; - private static final int MAX_GRAPH_VALUES_SIZE = 400; - /** Maximum time between values so that they are considered part of the same gesture. */ - private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1); - - private final DisplayMetrics mDm; - /** - * Distance in position units (amount scrolled in display pixels) from the center to the - * top/bottom frame lines. - */ - private final float mFrameCenterToBorderDistance; - private final float mScaledVerticalScrollFactor; - private final Locale mDefaultLocale = Locale.getDefault(); - private final Paint mFramePaint = new Paint(); - private final Paint mFrameTextPaint = new Paint(); - private final Paint mGraphLinePaint = new Paint(); - private final Paint mGraphPointPaint = new Paint(); - - private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE); - /** Position at which graph values are placed at the center of the graph. */ - private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; - - RotaryInputGraphView(Context c) { - super(c); - - mDm = mContext.getResources().getDisplayMetrics(); - // This makes the center-to-border distance equivalent to the display height, meaning - // that the total height of the graph is equivalent to 2x the display height. - mFrameCenterToBorderDistance = mDm.heightPixels; - mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); - - mFramePaint.setColor(FRAME_COLOR); - mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm)); - - mFrameTextPaint.setColor(GRAPH_COLOR); - mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm)); - - mGraphLinePaint.setColor(GRAPH_COLOR); - mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm)); - mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND); - mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND); - - mGraphPointPaint.setColor(GRAPH_COLOR); - mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm)); - mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND); - mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND); - } - - /** - * Reads new scroll axis value and updates the list accordingly. Old positions are - * kept at the front (what you would get with getFirst), while the recent positions are - * kept at the back (what you would get with getLast). Also updates the frame center - * position to handle out-of-bounds cases. - */ - void addValue(float scrollAxisValue, long eventTime) { - // Remove values that are too old. - while (mGraphValues.getSize() > 0 - && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) { - mGraphValues.removeFirst(); - } - - // If there are no recent values, reset the frame center. - if (mGraphValues.getSize() == 0) { - mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; - } - - // Handle new value. We multiply the scroll axis value by the scaled scroll factor to - // get the amount of pixels to be scrolled. We also compute the accumulated position - // by adding the current value to the last one (if not empty). - final float displacement = scrollAxisValue * mScaledVerticalScrollFactor; - final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos); - final float pos = prevPos + displacement; - - mGraphValues.add(pos, eventTime); - - // The difference between the distance of the most recent position from the center - // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center - // frame (mFrameCenterToBorderDistance). - final float verticalDiff = Math.abs(pos - mFrameCenterPosition) - - mFrameCenterToBorderDistance; - // If needed, translate frame. - if (verticalDiff > 0) { - final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1; - // Here, we update the center frame position by the exact amount needed for us to - // stay within the maximum allowed distance from the center frame. - mFrameCenterPosition += sign * verticalDiff; - } - - // Redraw canvas. - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Note: vertical coordinates in Canvas go from top to bottom, - // that is bottomY > middleY > topY. - final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm); - final int topY = verticalMargin; - final int bottomY = getHeight() - verticalMargin; - final int middleY = (topY + bottomY) / 2; - - // Note: horizontal coordinates in Canvas go from left to right, - // that is rightX > leftX. - final int leftX = 0; - final int rightX = getWidth(); - - // Draw the frame, which includes 3 lines that show the maximum, - // minimum and middle positions of the graph. - canvas.drawLine(leftX, topY, rightX, topY, mFramePaint); - canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint); - canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint); - - // Draw the position that each frame line corresponds to. - final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", - mFrameCenterPosition + mFrameCenterToBorderDistance), - leftX, - topY - frameTextOffset, mFrameTextPaint - ); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", mFrameCenterPosition), - leftX, - middleY - frameTextOffset, mFrameTextPaint - ); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", - mFrameCenterPosition - mFrameCenterToBorderDistance), - leftX, - bottomY - frameTextOffset, mFrameTextPaint - ); - - // If there are no graph values to be drawn, stop here. - if (mGraphValues.getSize() == 0) { - return; - } - - // Draw the graph using the times and positions. - // We start at the most recent value (which should be drawn at the right) and move - // to the older values (which should be drawn to the left of more recent ones). Negative - // indices are handled by circuling back to the end of the buffer. - final long mostRecentTime = mGraphValues.getLast().mTime; - float prevCoordX = 0; - float prevCoordY = 0; - float prevAge = 0; - for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) { - final GraphValue value = iter.next(); - - final int age = (int) (mostRecentTime - value.mTime); - final float pos = value.mPos; - - // We get the horizontal coordinate in time units from left to right with - // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas - // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL) - // and by multiplying it by the canvas length (rightX - leftX). Finally, we - // offset the coordinate by adding it to leftX. - final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age) - / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX); - - // We get the vertical coordinate in position units from middle to top with - // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas - // units by dividing it by half of the position-domain length - // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas - // length (middleY - topY). Finally, we offset the coordinate by subtracting - // it from middleY (we can't "add" here because the coordinate grows from top - // to bottom). - final float coordY = middleY - ((pos - mFrameCenterPosition) - / mFrameCenterToBorderDistance) * (middleY - topY); - - // Draw a point for this value. - canvas.drawPoint(coordX, coordY, mGraphPointPaint); - - // If this value is part of the same gesture as the previous one, draw a line - // between them. We ignore the first value (with age = 0). - if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) { - canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint); - } - - prevCoordX = coordX; - prevCoordY = coordY; - prevAge = age; - } - } - - @VisibleForTesting - float getFrameCenterPosition() { - return mFrameCenterPosition; - } - - /** - * Converts a dimension in scaled pixel units to integer display pixels. - */ - private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { - return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); - } - - /** - * Holds data needed to draw each entry in the graph. - */ - private static class GraphValue { - /** Position. */ - float mPos; - /** Time when this value was added. */ - long mTime; - - GraphValue(float pos, long time) { - this.mPos = pos; - this.mTime = time; - } - } - - /** - * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the - * old values with new ones to avoid creating new objects. - */ - private static class CyclicBuffer { - private final GraphValue[] mValues; - private final int mCapacity; - private int mSize = 0; - private int mLastIndex = 0; - - // The iteration index and counter are here to make it easier to reset them. - /** Determines the value currently pointed by the iterator. */ - private int mIteratorIndex; - /** Counts how many values have been iterated through. */ - private int mIteratorCount; - - /** Used traverse the values in reverse order. */ - private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() { - @Override - public boolean hasNext() { - return mIteratorCount <= mSize; - } - - @Override - public GraphValue next() { - // Returns the value currently pointed by the iterator and moves the iterator to - // the previous one. - mIteratorCount++; - return mValues[(mIteratorIndex-- + mCapacity) % mCapacity]; - } - }; - - CyclicBuffer(int capacity) { - mCapacity = capacity; - mValues = new GraphValue[capacity]; - } - - /** - * Add new graph value. If there is an existing object, we replace its data with the - * new one. With this, we re-use old objects instead of creating new ones. - */ - void add(float pos, long time) { - mLastIndex = (mLastIndex + 1) % mCapacity; - if (mValues[mLastIndex] == null) { - mValues[mLastIndex] = new GraphValue(pos, time); - } else { - final GraphValue oldValue = mValues[mLastIndex]; - oldValue.mPos = pos; - oldValue.mTime = time; - } - - // If needed, account for new value in the buffer size. - if (mSize != mCapacity) { - mSize++; - } - } - - int getSize() { - return mSize; - } - - GraphValue getFirst() { - final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1; - final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity; - return mValues[firstIndex]; - } - - GraphValue getLast() { - return mValues[mLastIndex]; - } - - void removeFirst() { - mSize--; - } - - /** Returns an iterator pointing at the last value. */ - Iterator<GraphValue> reverseIterator() { - mIteratorIndex = mLastIndex; - mIteratorCount = 1; - return mReverseIterator; - } - } -} diff --git a/services/core/java/com/android/server/input/debug/RotaryInputValueView.java b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java deleted file mode 100644 index 38613ebf8923..000000000000 --- a/services/core/java/com/android/server/input/debug/RotaryInputValueView.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 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.server.input.debug; - -import static android.util.TypedValue.COMPLEX_UNIT_SP; - -import android.content.Context; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Typeface; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.ViewConfiguration; -import android.widget.TextView; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Locale; - -/** Draws the most recent rotary input value and indicates whether the source is active. */ -class RotaryInputValueView extends TextView { - - private static final int INACTIVE_TEXT_COLOR = 0xffff00ff; - private static final int ACTIVE_TEXT_COLOR = 0xff420f28; - private static final int TEXT_SIZE_SP = 8; - private static final int SIDE_PADDING_SP = 4; - /** Determines how long the active status lasts. */ - private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */; - private static final ColorFilter ACTIVE_BACKGROUND_FILTER = - new ColorMatrixColorFilter(new float[]{ - 0, 0, 0, 0, 255, // red - 0, 0, 0, 0, 0, // green - 0, 0, 0, 0, 255, // blue - 0, 0, 0, 0, 200 // alpha - }); - - private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false); - private final float mScaledVerticalScrollFactor; - private final Locale mDefaultLocale = Locale.getDefault(); - - RotaryInputValueView(Context c) { - super(c); - - DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); - mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); - - setText(getFormattedValue(0)); - setTextColor(INACTIVE_TEXT_COLOR); - setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm)); - setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0, - applyDimensionSp(SIDE_PADDING_SP, dm), 0); - setTypeface(null, Typeface.BOLD); - setBackgroundResource(R.drawable.focus_event_rotary_input_background); - } - - /** Updates the shown text with the formatted value. */ - void updateValue(float value) { - removeCallbacks(mUpdateActivityStatusCallback); - - setText(getFormattedValue(value * mScaledVerticalScrollFactor)); - - updateActivityStatus(true); - postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); - } - - @VisibleForTesting - void updateActivityStatus(boolean active) { - if (active) { - setTextColor(ACTIVE_TEXT_COLOR); - getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); - } else { - setTextColor(INACTIVE_TEXT_COLOR); - getBackground().clearColorFilter(); - } - } - - private String getFormattedValue(float value) { - return String.format(mDefaultLocale, "%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); - } - - /** - * Converts a dimension in scaled pixel units to integer display pixels. - */ - private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { - return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); - } -} diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 567d8ac5e5df..f21a9fe6af2a 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -201,10 +201,10 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter this::onProviderEnabledChanged; private final SettingsHelper.GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener = - this::onBackgroundThrottlePackageWhitelistChanged; + this::onBackgroundThrottlePackageAllowlistChanged; private final SettingsHelper.UserSettingChangedListener mLocationPackageBlacklistChangedListener = - this::onLocationPackageBlacklistChanged; + this::onLocationPackageDenylistChanged; private final LocationPermissionsHelper.LocationPermissionsListener mLocationPermissionsListener = new LocationPermissionsHelper.LocationPermissionsListener() { @@ -407,11 +407,11 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } - private void onBackgroundThrottlePackageWhitelistChanged() { + private void onBackgroundThrottlePackageAllowlistChanged() { updateRegistrations(registration -> true); } - private void onLocationPackageBlacklistChanged(int userId) { + private void onLocationPackageDenylistChanged(int userId) { updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } diff --git a/services/core/java/com/android/server/location/injector/SettingsHelper.java b/services/core/java/com/android/server/location/injector/SettingsHelper.java index 490bfe1ab82f..32cbff8d50c7 100644 --- a/services/core/java/com/android/server/location/injector/SettingsHelper.java +++ b/services/core/java/com/android/server/location/injector/SettingsHelper.java @@ -93,37 +93,37 @@ public abstract class SettingsHelper { GlobalSettingChangedListener listener); /** - * Check if the given package is blacklisted for location access. + * Check if the given package is denylisted for location access. */ public abstract boolean isLocationPackageBlacklisted(int userId, String packageName); /** - * Add a listener for changes to the location package blacklist. Callbacks occur on an + * Add a listener for changes to the location package denylist. Callbacks occur on an * unspecified thread. */ public abstract void addOnLocationPackageBlacklistChangedListener( UserSettingChangedListener listener); /** - * Remove a listener for changes to the location package blacklist. + * Remove a listener for changes to the location package denylist. */ public abstract void removeOnLocationPackageBlacklistChangedListener( UserSettingChangedListener listener); /** - * Retrieve the background throttle package whitelist. + * Retrieve the background throttle package allowlist. */ public abstract Set<String> getBackgroundThrottlePackageWhitelist(); /** - * Add a listener for changes to the background throttle package whitelist. Callbacks occur on + * Add a listener for changes to the background throttle package allowlist. Callbacks occur on * an unspecified thread. */ public abstract void addOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener); /** - * Remove a listener for changes to the background throttle package whitelist. + * Remove a listener for changes to the background throttle package allowlist. */ public abstract void removeOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener); @@ -134,14 +134,14 @@ public abstract class SettingsHelper { public abstract boolean isGnssMeasurementsFullTrackingEnabled(); /** - * Add a listener for changes to the background throttle package whitelist. Callbacks occur on + * Add a listener for changes to the background throttle package allowlist. Callbacks occur on * an unspecified thread. */ public abstract void addOnGnssMeasurementsFullTrackingEnabledChangedListener( GlobalSettingChangedListener listener); /** - * Remove a listener for changes to the background throttle package whitelist. + * Remove a listener for changes to the background throttle package allowlist. */ public abstract void removeOnGnssMeasurementsFullTrackingEnabledChangedListener( GlobalSettingChangedListener listener); @@ -166,14 +166,14 @@ public abstract class SettingsHelper { public abstract PackageTagsList getIgnoreSettingsAllowlist(); /** - * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an + * Add a listener for changes to the ignore settings package allowlist. Callbacks occur on an * unspecified thread. */ public abstract void addIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener); /** - * Remove a listener for changes to the ignore settings package whitelist. + * Remove a listener for changes to the ignore settings package allowlist. */ public abstract void removeIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener); diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java index 777683ef59cf..8bb184c43ebb 100644 --- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java @@ -65,8 +65,8 @@ import java.util.function.Supplier; */ public class SystemSettingsHelper extends SettingsHelper { - private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist"; - private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist"; + private static final String LOCATION_PACKAGE_DENYLIST = "locationPackagePrefixBlacklist"; + private static final String LOCATION_PACKAGE_ALLOWLIST = "locationPackagePrefixWhitelist"; private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000; private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS = @@ -93,9 +93,9 @@ public class SystemSettingsHelper extends SettingsHelper { mGnssMeasurementFullTracking = new BooleanGlobalSetting(context, ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, FgThread.getHandler()); mLocationPackageBlacklist = new StringListCachedSecureSetting(context, - LOCATION_PACKAGE_BLACKLIST, FgThread.getHandler()); + LOCATION_PACKAGE_DENYLIST, FgThread.getHandler()); mLocationPackageWhitelist = new StringListCachedSecureSetting(context, - LOCATION_PACKAGE_WHITELIST, FgThread.getHandler()); + LOCATION_PACKAGE_ALLOWLIST, FgThread.getHandler()); mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context, LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java index 80d6ebbd90b3..2d58fe5a1678 100644 --- a/services/core/java/com/android/server/pm/PackageMetrics.java +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -34,11 +34,13 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Stream; /** * Metrics class for reporting stats to logging infrastructures like statsd @@ -155,10 +157,27 @@ final class PackageMetrics { private long getApksSize(File apkDir) { // TODO(b/249294752): also count apk sizes for failed installs final AtomicLong apksSize = new AtomicLong(); - try (Stream<Path> walkStream = Files.walk(apkDir.toPath())) { - walkStream.filter(p -> p.toFile().isFile() - && ApkLiteParseUtils.isApkFile(p.toFile())).forEach( - f -> apksSize.addAndGet(f.toFile().length())); + try { + Files.walkFileTree(apkDir.toPath(), new SimpleFileVisitor<>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + if (dir.equals(apkDir.toPath())) { + return FileVisitResult.CONTINUE; + } else { + return FileVisitResult.SKIP_SUBTREE; + } + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (file.toFile().isFile() && ApkLiteParseUtils.isApkFile(file.toFile())) { + apksSize.addAndGet(file.toFile().length()); + } + return FileVisitResult.CONTINUE; + } + }); } catch (IOException e) { // ignore } diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index 7ed10a4df1db..160b2aa05c18 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -516,6 +516,13 @@ final class ResolveIntentHelper { @PackageManager.ResolveInfoFlagsBits long flags, int userId) { if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); + + // Only if the service query is coming from the system process, + // it should be allowed to match quarantined components + if (callingUid != Process.SYSTEM_UID) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } + final String instantAppPkgName = computer.getInstantAppPackageName(callingUid); flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, false /* isImplicitImageCaptureIntentAndNotSetByDpc */); diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java index 153238fa6866..4e08106ed49a 100644 --- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java +++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java @@ -24,6 +24,7 @@ import android.util.Slog; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; +import com.android.server.pm.Flags; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -141,7 +142,8 @@ public final class SuspendParams { PersistableBundle readAppExtras = null; PersistableBundle readLauncherExtras = null; - final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false); + final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false) + && Flags.quarantinedEnabled(); final int currentDepth = in.getDepth(); int type; diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index e2cb87e72c7a..dc022f70e47c 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -3055,7 +3055,7 @@ public class ParsingPackageUtils { * * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse * call if requested. Leaving this as an optional method for the caller means we have to - * construct a dummy ParseInput. + * construct a placeholder ParseInput. */ @CheckResult public static ParseResult<SigningDetails> getSigningDetails(ParseInput input, diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index 33bed3d42e50..577468b0c749 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -56,6 +56,7 @@ import java.util.Objects; public final class HintManagerService extends SystemService { private static final String TAG = "HintManagerService"; private static final boolean DEBUG = false; + private static final int MAX_HINT_SESSION_COUNT_PER_UID = 20; @VisibleForTesting final long mHintSessionPreferredRate; // Multi-level map storing all active AppHintSessions. @@ -367,6 +368,23 @@ public final class HintManagerService extends SystemService { + " not be empty."); final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + int sessionCount = 0; + synchronized (mLock) { + ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = + mActiveSessions.get(callingUid); + if (tokenMap != null) { + for (ArraySet<AppHintSession> arr : tokenMap.values()) { + sessionCount += arr.size(); + } + } + } + if (sessionCount >= MAX_HINT_SESSION_COUNT_PER_UID) { + throw new IllegalStateException( + "Max session count limit reached: " + sessionCount); + } + } + final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid()); final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java index daf02ca81a10..f9d57e4c9042 100644 --- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java @@ -342,20 +342,24 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat @Override public Future<?> scheduleCleanupDueToRemovedUser(int userId) { synchronized (BatteryExternalStatsWorker.this) { - // Initial quick clean-up after a user removal - mExecutorService.schedule(() -> { - synchronized (mStats) { - mStats.clearRemovedUserUidsLocked(userId); - } - }, UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS); + try { + // Initial quick clean-up after a user removal + mExecutorService.schedule(() -> { + synchronized (mStats) { + mStats.clearRemovedUserUidsLocked(userId); + } + }, UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS); - // Final clean-up after a user removal, to take care of UIDs that were running longer - // than expected - return mExecutorService.schedule(() -> { - synchronized (mStats) { - mStats.clearRemovedUserUidsLocked(userId); - } - }, UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS); + // Final clean-up after a user removal, to take care of UIDs that were running + // longer than expected + return mExecutorService.schedule(() -> { + synchronized (mStats) { + mStats.clearRemovedUserUidsLocked(userId); + } + }, UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException e) { + return CompletableFuture.failedFuture(e); + } } } @@ -401,7 +405,11 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat scheduleSyncLocked("write", UPDATE_ALL); // Since we use a single threaded executor, we can assume the next scheduled task's // Future finishes after the sync. - return mExecutorService.submit(mWriteTask); + try { + return mExecutorService.submit(mWriteTask); + } catch (RejectedExecutionException e) { + return CompletableFuture.failedFuture(e); + } } /** @@ -429,7 +437,11 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat if (mCurrentFuture == null) { mUpdateFlags = flags; mCurrentReason = reason; - mCurrentFuture = mExecutorService.submit(mSyncTask); + try { + mCurrentFuture = mExecutorService.submit(mSyncTask); + } catch (RejectedExecutionException e) { + return CompletableFuture.failedFuture(e); + } } mUpdateFlags |= flags; return mCurrentFuture; diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 0ca560398657..2007079ea5ca 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -121,7 +121,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } else if (getAvailableRollback(failedPackage) != null) { // Rollback is available, we may get a callback into #execute - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_60; + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } else if (anyRollbackAvailable) { // If any rollbacks are available, we will commit them impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index 70f20075b48c..cdd1a2699e5b 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -161,9 +161,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { final boolean visible = (window.inputConfig & InputConfig.NOT_VISIBLE) == 0; final boolean isNotClone = (window.inputConfig & InputConfig.CLONE) == 0; final boolean hasTouchableRegion = !window.touchableRegion.isEmpty(); - final boolean hasNonEmptyFrame = - (window.frameBottom != window.frameTop) && (window.frameLeft - != window.frameRight); + final boolean hasNonEmptyFrame = !window.frame.isEmpty(); if (visible && isNotClone && hasTouchableRegion && hasNonEmptyFrame) { tempVisibleWindows.add(window); } @@ -694,9 +692,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null && controller.shouldIgnoreForAccessibility(windowState); - final Rect windowFrame = new Rect(inputWindowHandle.frameLeft, - inputWindowHandle.frameTop, inputWindowHandle.frameRight, - inputWindowHandle.frameBottom); + final Rect windowFrame = new Rect(inputWindowHandle.frame); getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion, instance.mTouchableRegionInWindow, windowFrame, magnificationInverseMatrix, displayMatrix); diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 3d4e0ebac258..64b7a6064e45 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -227,18 +227,6 @@ class InputWindowHandleWrapper { mChanged = true; } - void setFrame(int left, int top, int right, int bottom) { - if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right - && mHandle.frameBottom == bottom) { - return; - } - mHandle.frameLeft = left; - mHandle.frameTop = top; - mHandle.frameRight = right; - mHandle.frameBottom = bottom; - mChanged = true; - } - void setSurfaceInset(int inset) { if (mHandle.surfaceInset == inset) { return; diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java index 35d4ffdd94d0..a14073006c31 100644 --- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java @@ -145,7 +145,7 @@ public class RollbackPackageHealthObserverTest { observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1)); // non-native crash for the package - assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_60, + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, observer.onHealthCheckFailed(testFailedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); // non-native crash for a different package diff --git a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java index 90df786e5970..45e7f3512975 100644 --- a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java +++ b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java @@ -34,10 +34,10 @@ import libcore.io.IoUtils; */ public class CertBlacklisterTest extends AndroidTestCase { - private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; + private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; - public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt"; - public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt"; + public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt"; + public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt"; public static final String PUBKEY_KEY = "pubkey_blacklist"; public static final String SERIAL_KEY = "serial_blacklist"; diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 9ca84d33998c..ce15c6d30531 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -218,7 +218,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void updateRuleSet_notSystemApp() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(false); Rule rule = new Rule( @@ -237,7 +237,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void updateRuleSet_authorized() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); Rule rule = new Rule( @@ -251,7 +251,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void updateRuleSet_correctMethodCall() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); IntentSender mockReceiver = mock(IntentSender.class); List<Rule> rules = @@ -271,7 +271,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void updateRuleSet_fail() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any()); IntentSender mockReceiver = mock(IntentSender.class); @@ -292,7 +292,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void broadcastReceiverRegistration() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); @@ -308,7 +308,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_correctArgs() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -345,7 +345,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_correctArgs_multipleCerts() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -368,7 +368,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_correctArgs_sourceStamp() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -393,7 +393,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_allow() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -412,7 +412,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_reject() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -438,7 +438,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_notInitialized() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); when(mIntegrityFileManager.initialized()).thenReturn(false); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = @@ -459,7 +459,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void verifierAsInstaller_skipIntegrityVerification() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); setIntegrityCheckIncludesRuleProvider(false); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = @@ -480,7 +480,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void getCurrentRules() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY); when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule)); @@ -490,7 +490,7 @@ public class AppIntegrityManagerServiceImplTest { @Test public void getWhitelistedRuleProviders_returnsEmptyForNonSystemApps() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(false); assertThat(mService.getWhitelistedRuleProviders()).isEmpty(); @@ -498,13 +498,13 @@ public class AppIntegrityManagerServiceImplTest { @Test public void getWhitelistedRuleProviders() throws Exception { - whitelistUsAsRuleProvider(); + allowlistUsAsRuleProvider(); makeUsSystemApp(); assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE); } - private void whitelistUsAsRuleProvider() { + private void allowlistUsAsRuleProvider() { Resources mockResources = mock(Resources.class); when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages)) .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE}); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index e5909a42ea10..80fb5e3f950d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -512,7 +512,7 @@ public class KeySyncTaskTest { verify(mTestOnlyInsecureCertificateHelper, atLeast(1)) .isTestOnlyCertificateAlias(eq(TEST_ROOT_CERT_ALIAS)); - // no whitelists check + // no allowlists check verify(mTestOnlyInsecureCertificateHelper, never()) .doesCredentialSupportInsecureMode(anyInt(), any()); verify(mTestOnlyInsecureCertificateHelper, never()) diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java index 07d4065e4f8f..a8e3c7e4ef6d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java @@ -117,7 +117,7 @@ public class UserSystemPackageInstallerTest { for (int userId : mRemoveUsers) { um.removeUser(userId); } - setUserTypePackageWhitelistMode(mOriginalWhitelistMode); + setUserTypePackageAllowlistMode(mOriginalWhitelistMode); } /** @@ -184,7 +184,7 @@ public class UserSystemPackageInstallerTest { } } - final ArrayMap<String, Long> expectedOutput = getNewPackageToWhitelistedBitSetMap(); + final ArrayMap<String, Long> expectedOutput = getNewPackageToAllowlistedBitSetMap(); expectedOutput.put("com.android.package1", expectedUserTypeBitSet1); expectedOutput.put("com.android.package2", expectedUserTypeBitSet2); expectedOutput.put("com.android.package3", expectedUserTypeBitSet3); @@ -227,7 +227,7 @@ public class UserSystemPackageInstallerTest { } }; - final ArrayMap<String, Long> expectedOutput = getNewPackageToWhitelistedBitSetMap(); + final ArrayMap<String, Long> expectedOutput = getNewPackageToAllowlistedBitSetMap(); expectedOutput.put("com.android.package2", 0L); expectedOutput.put("com.android.package3", 0L); expectedOutput.put("com.android.package4", 0L); @@ -340,7 +340,7 @@ public class UserSystemPackageInstallerTest { public void testPackagesForCreateUser_full() { final String userTypeToCreate = USER_TYPE_FULL_SECONDARY; final long userTypeMask = mUserSystemPackageInstaller.getUserTypeMask(userTypeToCreate); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); PackageManager pm = mContext.getPackageManager(); final SystemConfig sysConfig = new SystemConfigTestClass(true); @@ -384,7 +384,7 @@ public class UserSystemPackageInstallerTest { */ @Test public void testInstallOverlayPackagesExplicitMode() { - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); final String[] userTypes = new String[]{"type"}; final long maskOfType = 0b0001L; @@ -453,49 +453,49 @@ public class UserSystemPackageInstallerTest { */ @Test public void testSetWhitelistEnabledMode() { - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertFalse(mUserSystemPackageInstaller.isEnforceMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG); assertTrue(mUserSystemPackageInstaller.isLogMode()); assertFalse(mUserSystemPackageInstaller.isEnforceMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertTrue(mUserSystemPackageInstaller.isEnforceMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertFalse(mUserSystemPackageInstaller.isEnforceMode()); assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertFalse(mUserSystemPackageInstaller.isEnforceMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA); + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertFalse(mUserSystemPackageInstaller.isEnforceMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode()); assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertTrue(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode( + setUserTypePackageAllowlistMode( USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); assertTrue(mUserSystemPackageInstaller.isLogMode()); assertTrue(mUserSystemPackageInstaller.isEnforceMode()); @@ -503,7 +503,7 @@ public class UserSystemPackageInstallerTest { assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode()); assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode()); - setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST + setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE); assertFalse(mUserSystemPackageInstaller.isLogMode()); assertTrue(mUserSystemPackageInstaller.isEnforceMode()); @@ -513,7 +513,7 @@ public class UserSystemPackageInstallerTest { } /** Sets the allowlist mode to the desired value via adb's setprop. */ - private void setUserTypePackageWhitelistMode(int mode) { + private void setUserTypePackageAllowlistMode(int mode) { UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); try { String result = uiDevice.executeShellCommand(String.format("setprop %s %d", @@ -526,7 +526,7 @@ public class UserSystemPackageInstallerTest { } /** @see UserSystemPackageInstaller#mWhitelistedPackagesForUserTypes */ - private ArrayMap<String, Long> getNewPackageToWhitelistedBitSetMap() { + private ArrayMap<String, Long> getNewPackageToAllowlistedBitSetMap() { final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); // "android" is always treated as allowlisted for all types, regardless of the xml file. pkgBitSetMap.put("android", ~0L); diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt index 560a91974692..20c0b0a3af66 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt @@ -529,7 +529,6 @@ class AndroidPackageParsingValidationTest { "}", ":", "?", - "-", "%", "^", "*", diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java index 9fca513e50b9..ee3ab9744fdd 100644 --- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -149,6 +149,32 @@ public class HintManagerServiceTest { } @Test + public void testCreateHintSession_exceedsLimit() throws Exception { + HintManagerService service = createService(); + IBinder token1 = new Binder(); + IBinder token2 = new Binder(); + + for (int i = 0; i < 10; i++) { + IHintSession a = service.getBinderServiceInstance().createHintSession(token1, + SESSION_TIDS_A, DEFAULT_TARGET_DURATION); + assertNotNull(a); + } + + for (int i = 0; i < 10; i++) { + IHintSession b = service.getBinderServiceInstance().createHintSession(token2, + SESSION_TIDS_B, DEFAULT_TARGET_DURATION); + assertNotNull(b); + } + + assertThrows(IllegalStateException.class, + () -> service.getBinderServiceInstance().createHintSession(token1, SESSION_TIDS_A, + DEFAULT_TARGET_DURATION)); + assertThrows(IllegalStateException.class, + () -> service.getBinderServiceInstance().createHintSession(token2, SESSION_TIDS_B, + DEFAULT_TARGET_DURATION)); + } + + @Test public void testPauseResumeHintSession() throws Exception { HintManagerService service = createService(); IBinder token = new Binder(); diff --git a/services/tests/servicestests/src/com/android/server/power/hint/OWNERS b/services/tests/servicestests/src/com/android/server/power/hint/OWNERS new file mode 100644 index 000000000000..c28c07a234b3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/hint/OWNERS @@ -0,0 +1,2 @@ +include /ADPF_OWNERS + diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 5bdcdf4d63ef..eb0a1e1a40bc 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -361,18 +361,6 @@ public final class Call { "android.telecom.extra.DIAGNOSTIC_MESSAGE"; /** - * Event reported from the Telecom stack to indicate that the {@link Connection} is not able to - * find any network and likely will not get connected. Upon receiving this event, the dialer - * app should show satellite SOS button if satellite is provisioned. - * <p> - * The dialer app receives this event via - * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. - * @hide - */ - public static final String EVENT_DISPLAY_SOS_MESSAGE = - "android.telecom.event.DISPLAY_SOS_MESSAGE"; - - /** * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this * call because they have declined to answer it. This typically means that they are unable * to answer the call at this time and would prefer it be sent to voicemail. diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index 6997f3c79bc3..f9844bcc677d 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -362,7 +362,6 @@ public final class DisconnectCause { /** * Indicates that the call was unable to be made because the satellite modem is enabled. - * @hide */ public static final int SATELLITE_ENABLED = 82; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f1ee76e1ed94..5f67441b533c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -76,7 +76,9 @@ import android.provider.Settings.SettingNotFoundException; import android.service.carrier.CarrierIdentifier; import android.service.carrier.CarrierService; import android.sysprop.TelephonyProperties; +import android.telecom.Call; import android.telecom.CallScreeningService; +import android.telecom.Connection; import android.telecom.InCallService; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; @@ -1186,6 +1188,17 @@ public class TelephonyManager { "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION"; /** + * Event reported from the Telephony stack to indicate that the {@link Connection} is not + * able to find any network and likely will not get connected. Upon receiving this event, + * the dialer app should show satellite SOS button if satellite is provisioned. + * <p> + * The dialer app receives this event via + * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. + */ + public static final String EVENT_DISPLAY_SOS_MESSAGE = + "android.telephony.event.DISPLAY_SOS_MESSAGE"; + + /** * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates * the type of supplementary service notification which occurred. * Will be either diff --git a/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java index ae7fb3b29f6c..1b98887199e3 100644 --- a/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java +++ b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java @@ -14,16 +14,15 @@ * limitations under the License. */ -package com.android.server.input.debug; +package com.android.server.input; -import static org.mockito.Mockito.anyFloat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -32,12 +31,11 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; +import android.view.ViewConfiguration; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.server.input.InputManagerService; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,50 +48,76 @@ import org.junit.runner.RunWith; public class FocusEventDebugViewTest { private FocusEventDebugView mFocusEventDebugView; - private RotaryInputValueView mRotaryInputValueView; - private RotaryInputGraphView mRotaryInputGraphView; + private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView; + private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView; + private float mScaledVerticalScrollFactor; @Before public void setUp() throws Exception { Context context = InstrumentationRegistry.getContext(); + mScaledVerticalScrollFactor = + ViewConfiguration.get(context).getScaledVerticalScrollFactor(); InputManagerService mockService = mock(InputManagerService.class); when(mockService.monitorInput(anyString(), anyInt())) .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]); - mRotaryInputValueView = spy(new RotaryInputValueView(context)); - mRotaryInputGraphView = spy(new RotaryInputGraphView(context)); + mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context); + mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context); mFocusEventDebugView = new FocusEventDebugView(context, mockService, () -> mRotaryInputValueView, () -> mRotaryInputGraphView); } @Test - public void handleRotaryInput_sendsMotionEventWhenEnabled() { + public void startsRotaryInputValueViewWithDefaultValue() { + assertEquals("+0.0", mRotaryInputValueView.getText()); + } + + @Test + public void startsRotaryInputGraphViewWithDefaultFrameCenter() { + assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); + } + + @Test + public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() { + mFocusEventDebugView.handleUpdateShowRotaryInput(true); + + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f)); + + assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor), + mRotaryInputValueView.getText()); + } + + @Test + public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() { mFocusEventDebugView.handleUpdateShowRotaryInput(true); - mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f)); - verify(mRotaryInputGraphView).addValue(0.5f, 10L); - verify(mRotaryInputValueView).updateValue(0.5f); + assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); } @Test - public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() { - mFocusEventDebugView.handleUpdateShowRotaryInput(false); + public void updateActivityStatus_setsAndRemovesColorFilter() { + // It should not be active initially. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); - mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); + mRotaryInputValueView.updateActivityStatus(true); + // It should be active after rotary input. + assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); - verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong()); - verify(mRotaryInputValueView, never()).updateValue(anyFloat()); + mRotaryInputValueView.updateActivityStatus(false); + // It should not be active after waiting for mUpdateActivityStatusCallback. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); } - private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) { + private MotionEvent createRotaryMotionEvent(float scrollAxisValue) { PointerCoords pointerCoords = new PointerCoords(); pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue); PointerProperties pointerProperties = new PointerProperties(); return MotionEvent.obtain( /* downTime */ 0, - /* eventTime */ eventTime, + /* eventTime */ 0, /* action */ MotionEvent.ACTION_SCROLL, /* pointerCount */ 1, /* pointerProperties */ new PointerProperties[] {pointerProperties}, diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java deleted file mode 100644 index af6ece414fd1..000000000000 --- a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 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.server.input.debug; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.content.Context; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Build/Install/Run: - * atest RotaryInputGraphViewTest - */ -@RunWith(AndroidJUnit4.class) -public class RotaryInputGraphViewTest { - - private RotaryInputGraphView mRotaryInputGraphView; - - @Before - public void setUp() throws Exception { - Context context = InstrumentationRegistry.getContext(); - - mRotaryInputGraphView = new RotaryInputGraphView(context); - } - - @Test - public void startsWithDefaultFrameCenter() { - assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); - } - - @Test - public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() { - final float scrollAxisValue = 1000f; - final long eventTime = 0; - - mRotaryInputGraphView.addValue(scrollAxisValue, eventTime); - - assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); - } -} diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java deleted file mode 100644 index e5e3852dc318..000000000000 --- a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 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.server.input.debug; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import android.content.Context; -import android.view.ViewConfiguration; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Locale; - -/** - * Build/Install/Run: - * atest RotaryInputValueViewTest - */ -@RunWith(AndroidJUnit4.class) -public class RotaryInputValueViewTest { - - private final Locale mDefaultLocale = Locale.getDefault(); - - private RotaryInputValueView mRotaryInputValueView; - private float mScaledVerticalScrollFactor; - - @Before - public void setUp() throws Exception { - Context context = InstrumentationRegistry.getContext(); - mScaledVerticalScrollFactor = - ViewConfiguration.get(context).getScaledVerticalScrollFactor(); - - mRotaryInputValueView = new RotaryInputValueView(context); - } - - @Test - public void startsWithDefaultValue() { - assertEquals("+0.0", mRotaryInputValueView.getText().toString()); - } - - @Test - public void updateValue_updatesTextWithScrollValue() { - final float scrollAxisValue = 1000f; - final String expectedText = String.format(mDefaultLocale, "+%.1f", - scrollAxisValue * mScaledVerticalScrollFactor); - - mRotaryInputValueView.updateValue(scrollAxisValue); - - assertEquals(expectedText, mRotaryInputValueView.getText().toString()); - } - - @Test - public void updateActivityStatus_setsAndRemovesColorFilter() { - // It should not be active initially. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); - - mRotaryInputValueView.updateActivityStatus(true); - // It should be active after rotary input. - assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); - - mRotaryInputValueView.updateActivityStatus(false); - // It should not be active after waiting for mUpdateActivityStatusCallback. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); - } -} |