diff options
150 files changed, 3736 insertions, 1584 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index a7f69374ff00..9955112649d3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -42056,6 +42056,7 @@ package android.telecom { method public void disconnect(@NonNull android.telecom.DisconnectCause, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method @NonNull public android.os.ParcelUuid getCallId(); method public void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); + method public void sendEvent(@NonNull String, @NonNull android.os.Bundle); method public void setActive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void setInactive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void startCallStreaming(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); @@ -42102,6 +42103,7 @@ package android.telecom { method public void onAvailableCallEndpointsChanged(@NonNull java.util.List<android.telecom.CallEndpoint>); method public void onCallEndpointChanged(@NonNull android.telecom.CallEndpoint); method public void onCallStreamingFailed(int); + method public void onEvent(@NonNull String, @NonNull android.os.Bundle); method public void onMuteStateChanged(boolean); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 675324fe6c28..def3fa93ce3c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1444,6 +1444,7 @@ package android.hardware.display { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode(); field public static final String DISPLAY_CATEGORY_REAR = "android.hardware.display.category.REAR"; + field public static final String HDR_OUTPUT_CONTROL_FLAG = "enable_hdr_output_control"; field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2 field public static final int SWITCHING_TYPE_NONE = 0; // 0x0 field public static final int SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY = 3; // 0x3 diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 87c77c2e8602..ef5cd9334be7 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5249,17 +5249,17 @@ public class Notification implements Parcelable boolean hasSecondLine = showProgress; if (p.hasTitle()) { contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE); - contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.title)); + contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.mTitle)); setTextViewColorPrimary(contentView, p.mTitleViewId, p); } else if (p.mTitleViewId != R.id.title) { // This alternate title view ID is not cleared by resetStandardTemplate contentView.setViewVisibility(p.mTitleViewId, View.GONE); contentView.setTextViewText(p.mTitleViewId, null); } - if (p.text != null && p.text.length() != 0 + if (p.mText != null && p.mText.length() != 0 && (!showProgress || p.mAllowTextWithProgress)) { contentView.setViewVisibility(p.mTextViewId, View.VISIBLE); - contentView.setTextViewText(p.mTextViewId, processTextSpans(p.text)); + contentView.setTextViewText(p.mTextViewId, processTextSpans(p.mText)); setTextViewColorSecondary(contentView, p.mTextViewId, p); hasSecondLine = true; } else if (p.mTextViewId != R.id.text) { @@ -5533,20 +5533,20 @@ public class Notification implements Parcelable if (p.mHideSubText) { return false; } - CharSequence summaryText = p.summaryText; - if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet + CharSequence headerText = p.mSubText; + if (headerText == null && mStyle != null && mStyle.mSummaryTextSet && mStyle.hasSummaryInHeader()) { - summaryText = mStyle.mSummaryText; + headerText = mStyle.mSummaryText; } - if (summaryText == null + if (headerText == null && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) { - summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); + headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); } - if (!TextUtils.isEmpty(summaryText)) { + if (!TextUtils.isEmpty(headerText)) { // TODO: Remove the span entirely to only have the string with propper formating. contentView.setTextViewText(R.id.header_text, processTextSpans( - processLegacyText(summaryText))); + processLegacyText(headerText))); setTextViewColorSecondary(contentView, R.id.header_text, p); contentView.setViewVisibility(R.id.header_text, View.VISIBLE); if (hasTextToLeft) { @@ -5566,9 +5566,9 @@ public class Notification implements Parcelable if (p.mHideSubText) { return false; } - if (!TextUtils.isEmpty(p.headerTextSecondary)) { + if (!TextUtils.isEmpty(p.mHeaderTextSecondary)) { contentView.setTextViewText(R.id.header_text_secondary, processTextSpans( - processLegacyText(p.headerTextSecondary))); + processLegacyText(p.mHeaderTextSecondary))); setTextViewColorSecondary(contentView, R.id.header_text_secondary, p); contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE); if (hasTextToLeft) { @@ -6175,7 +6175,7 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED) .highlightExpander(false) .fillTextsFrom(this); - if (!useRegularSubtext || TextUtils.isEmpty(p.summaryText)) { + if (!useRegularSubtext || TextUtils.isEmpty(p.mSubText)) { p.summaryText(createSummaryText()); } RemoteViews header = makeNotificationHeader(p); @@ -7192,7 +7192,7 @@ public class Notification implements Parcelable checkBuilder(); if (mBigContentTitle != null) { - p.title = mBigContentTitle; + p.mTitle = mBigContentTitle; } return mBuilder.applyStandardTemplateWithActions(layoutId, p, result); @@ -12455,10 +12455,10 @@ public class Notification implements Parcelable boolean mAllowTextWithProgress; int mTitleViewId; int mTextViewId; - CharSequence title; - CharSequence text; - CharSequence headerTextSecondary; - CharSequence summaryText; + @Nullable CharSequence mTitle; + @Nullable CharSequence mText; + @Nullable CharSequence mHeaderTextSecondary; + @Nullable CharSequence mSubText; int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; boolean allowColorization = true; boolean mHighlightExpander = false; @@ -12480,10 +12480,10 @@ public class Notification implements Parcelable mAllowTextWithProgress = false; mTitleViewId = R.id.title; mTextViewId = R.id.text; - title = null; - text = null; - summaryText = null; - headerTextSecondary = null; + mTitle = null; + mText = null; + mSubText = null; + mHeaderTextSecondary = null; maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; allowColorization = true; mHighlightExpander = false; @@ -12491,7 +12491,7 @@ public class Notification implements Parcelable } final boolean hasTitle() { - return !TextUtils.isEmpty(title) && !mHideTitle; + return !TextUtils.isEmpty(mTitle) && !mHideTitle; } final StandardTemplateParams viewType(int viewType) { @@ -12564,23 +12564,23 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams title(CharSequence title) { - this.title = title; + final StandardTemplateParams title(@Nullable CharSequence title) { + this.mTitle = title; return this; } - final StandardTemplateParams text(CharSequence text) { - this.text = text; + final StandardTemplateParams text(@Nullable CharSequence text) { + this.mText = text; return this; } - final StandardTemplateParams summaryText(CharSequence text) { - this.summaryText = text; + final StandardTemplateParams summaryText(@Nullable CharSequence text) { + this.mSubText = text; return this; } - final StandardTemplateParams headerTextSecondary(CharSequence text) { - this.headerTextSecondary = text; + final StandardTemplateParams headerTextSecondary(@Nullable CharSequence text) { + this.mHeaderTextSecondary = text; return this; } @@ -12607,9 +12607,9 @@ public class Notification implements Parcelable final StandardTemplateParams fillTextsFrom(Builder b) { Bundle extras = b.mN.extras; - this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE)); - this.text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT)); - this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT); + this.mTitle = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE)); + this.mText = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT)); + this.mSubText = extras.getCharSequence(EXTRA_SUB_TEXT); return this; } diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 77072890a1eb..856bde870bcf 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -70,6 +70,12 @@ public final class ContentCaptureOptions implements Parcelable { public final int logHistorySize; /** + * Disable flush when receiving a VIEW_TREE_APPEARING event. + * @hide + */ + public final boolean disableFlushForViewTreeAppearing; + + /** * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). */ @@ -90,7 +96,8 @@ public final class ContentCaptureOptions implements Parcelable { public ContentCaptureOptions(int loggingLevel) { this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, - /* logHistorySize= */ 0, /* whitelistedComponents= */ null); + /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, + /* whitelistedComponents= */ null); } /** @@ -98,10 +105,23 @@ public final class ContentCaptureOptions implements Parcelable { */ public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, - @SuppressLint("NullableCollection") + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + @Nullable ArraySet<ComponentName> whitelistedComponents) { + this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, logHistorySize, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); + } + + /** @hide */ + public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable ArraySet<ComponentName> whitelistedComponents) { this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents); + textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, + whitelistedComponents); } /** @hide */ @@ -111,11 +131,14 @@ public final class ContentCaptureOptions implements Parcelable { ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, - ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents); + ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); } private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, @Nullable ArraySet<ComponentName> whitelistedComponents) { this.lite = lite; this.loggingLevel = loggingLevel; @@ -123,6 +146,7 @@ public final class ContentCaptureOptions implements Parcelable { this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; this.logHistorySize = logHistorySize; + this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; this.whitelistedComponents = whitelistedComponents; } @@ -171,7 +195,8 @@ public final class ContentCaptureOptions implements Parcelable { .append(", maxBufferSize=").append(maxBufferSize) .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) - .append(", logHistorySize=").append(logHistorySize); + .append(", logHistorySize=").append(logHistorySize) + .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { string.append(", whitelisted=").append(whitelistedComponents); } @@ -189,6 +214,7 @@ public final class ContentCaptureOptions implements Parcelable { pw.print(", idle="); pw.print(idleFlushingFrequencyMs); pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); pw.print(", logSize="); pw.print(logHistorySize); + pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { pw.print(", whitelisted="); pw.print(whitelistedComponents); } @@ -209,6 +235,7 @@ public final class ContentCaptureOptions implements Parcelable { parcel.writeInt(idleFlushingFrequencyMs); parcel.writeInt(textChangeFlushingFrequencyMs); parcel.writeInt(logHistorySize); + parcel.writeBoolean(disableFlushForViewTreeAppearing); parcel.writeArraySet(whitelistedComponents); } @@ -226,12 +253,13 @@ public final class ContentCaptureOptions implements Parcelable { final int idleFlushingFrequencyMs = parcel.readInt(); final int textChangeFlushingFrequencyMs = parcel.readInt(); final int logHistorySize = parcel.readInt(); + final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); @SuppressWarnings("unchecked") final ArraySet<ComponentName> whitelistedComponents = (ArraySet<ComponentName>) parcel.readArraySet(null); return new ContentCaptureOptions(loggingLevel, maxBufferSize, idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, - whitelistedComponents); + disableFlushForViewTreeAppearing, whitelistedComponents); } @Override diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index b72db5bdafa3..795c77ff5105 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -363,7 +363,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall enumCheckUriPermission, callingUid, uri.getAuthority(), type); } - } catch (RemoteException e) { + } catch (Exception e) { //does nothing } } else { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 7766896c7367..8acdf510484b 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -3981,8 +3981,7 @@ public class PackageInstaller { /** * Details for requesting the pre-commit install approval. */ - @DataClass(genParcelable = true, genHiddenConstructor = true, genBuilder = true, - genToString = true) + @DataClass(genConstructor = false, genToString = true) public static final class PreapprovalDetails implements Parcelable { /** * The icon representing the app to be installed. @@ -4001,22 +4000,6 @@ public class PackageInstaller { */ private final @NonNull String mPackageName; - - - - // Code below generated by codegen v1.0.23. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/PackageInstaller.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - /** * Creates a new PreapprovalDetails. * @@ -4030,78 +4013,25 @@ public class PackageInstaller { * The package name of the app to be installed. * @hide */ - @DataClass.Generated.Member public PreapprovalDetails( @Nullable Bitmap icon, @NonNull CharSequence label, @NonNull ULocale locale, @NonNull String packageName) { - this.mIcon = icon; - this.mLabel = label; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mLabel); - this.mLocale = locale; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mLocale); - this.mPackageName = packageName; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mPackageName); - - // onConstructed(); // You can define this method to get a callback - } - - /** - * The icon representing the app to be installed. - */ - @DataClass.Generated.Member - public @Nullable Bitmap getIcon() { - return mIcon; - } - - /** - * The label representing the app to be installed. - */ - @DataClass.Generated.Member - public @NonNull CharSequence getLabel() { - return mLabel; - } - - /** - * The locale of the app label being used. - */ - @DataClass.Generated.Member - public @NonNull ULocale getLocale() { - return mLocale; - } - - /** - * The package name of the app to be installed. - */ - @DataClass.Generated.Member - public @NonNull String getPackageName() { - return mPackageName; + mIcon = icon; + mLabel = label; + Preconditions.checkArgument(!TextUtils.isEmpty(mLabel), + "App label cannot be empty."); + mLocale = locale; + Preconditions.checkArgument(!Objects.isNull(mLocale), + "Locale cannot be null."); + mPackageName = packageName; + Preconditions.checkArgument(!TextUtils.isEmpty(mPackageName), + "Package name cannot be empty."); } @Override - @DataClass.Generated.Member - public String toString() { - // You can override field toString logic by defining methods like: - // String fieldNameToString() { ... } - - return "PreapprovalDetails { " + - "icon = " + mIcon + ", " + - "label = " + mLabel + ", " + - "locale = " + mLocale + ", " + - "packageName = " + mPackageName + - " }"; - } - - @Override - @DataClass.Generated.Member public void writeToParcel(@NonNull Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - byte flg = 0; if (mIcon != null) flg |= 0x1; dest.writeByte(flg); @@ -4112,37 +4042,28 @@ public class PackageInstaller { } @Override - @DataClass.Generated.Member public int describeContents() { return 0; } /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member /* package-private */ PreapprovalDetails(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - byte flg = in.readByte(); - Bitmap icon = (flg & 0x1) == 0 ? null : Bitmap.CREATOR.createFromParcel(in); - CharSequence label = (CharSequence) in.readCharSequence(); - ULocale locale = new ULocale(in.readString8()); - String packageName = in.readString8(); - - this.mIcon = icon; - this.mLabel = label; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mLabel); - this.mLocale = locale; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mLocale); - this.mPackageName = packageName; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mPackageName); - - // onConstructed(); // You can define this method to get a callback + final Bitmap icon = (flg & 0x1) == 0 ? null : Bitmap.CREATOR.createFromParcel(in); + final CharSequence label = in.readCharSequence(); + final ULocale locale = new ULocale(in.readString8()); + final String packageName = in.readString8(); + + mIcon = icon; + mLabel = label; + Preconditions.checkArgument(!TextUtils.isEmpty(mLabel), + "App label cannot be empty."); + mLocale = locale; + Preconditions.checkArgument(!Objects.isNull(mLocale), + "Locale cannot be null."); + mPackageName = packageName; + Preconditions.checkArgument(!TextUtils.isEmpty(mPackageName), + "Package name cannot be empty."); } - @DataClass.Generated.Member public static final @NonNull Parcelable.Creator<PreapprovalDetails> CREATOR = new Parcelable.Creator<PreapprovalDetails>() { @Override @@ -4159,8 +4080,6 @@ public class PackageInstaller { /** * A builder for {@link PreapprovalDetails} */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member public static final class Builder { private @Nullable Bitmap mIcon; @@ -4178,7 +4097,6 @@ public class PackageInstaller { /** * The icon representing the app to be installed. */ - @DataClass.Generated.Member public @NonNull Builder setIcon(@NonNull Bitmap value) { checkNotUsed(); mBuilderFieldsSet |= 0x1; @@ -4189,7 +4107,6 @@ public class PackageInstaller { /** * The label representing the app to be installed. */ - @DataClass.Generated.Member public @NonNull Builder setLabel(@NonNull CharSequence value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; @@ -4200,7 +4117,6 @@ public class PackageInstaller { /** * The locale of the app label being used. */ - @DataClass.Generated.Member public @NonNull Builder setLocale(@NonNull ULocale value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; @@ -4211,7 +4127,6 @@ public class PackageInstaller { /** * The package name of the app to be installed. */ - @DataClass.Generated.Member public @NonNull Builder setPackageName(@NonNull String value) { checkNotUsed(); mBuilderFieldsSet |= 0x8; @@ -4234,17 +4149,79 @@ public class PackageInstaller { private void checkNotUsed() { if ((mBuilderFieldsSet & 0x10) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); + throw new IllegalStateException("This Builder should not be reused. " + + "Use a new Builder instance instead"); } } } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/PackageInstaller.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * The icon representing the app to be installed. + */ + @DataClass.Generated.Member + public @Nullable Bitmap getIcon() { + return mIcon; + } + + /** + * The label representing the app to be installed. + */ + @DataClass.Generated.Member + public @NonNull CharSequence getLabel() { + return mLabel; + } + + /** + * The locale of the app label being used. + */ + @DataClass.Generated.Member + public @NonNull ULocale getLocale() { + return mLocale; + } + + /** + * The package name of the app to be installed. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "PreapprovalDetails { " + + "icon = " + mIcon + ", " + + "label = " + mLabel + ", " + + "locale = " + mLocale + ", " + + "packageName = " + mPackageName + + " }"; + } + @DataClass.Generated( - time = 1666748098353L, + time = 1676970504308L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/PackageInstaller.java", - inputSignatures = "private final @android.annotation.Nullable android.graphics.Bitmap mIcon\nprivate final @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate final @android.annotation.NonNull android.icu.util.ULocale mLocale\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nclass PreapprovalDetails extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genHiddenConstructor=true, genBuilder=true, genToString=true)") + inputSignatures = "private final @android.annotation.Nullable android.graphics.Bitmap mIcon\nprivate final @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate final @android.annotation.NonNull android.icu.util.ULocale mLocale\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.PackageInstaller.PreapprovalDetails> CREATOR\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override int describeContents()\nclass PreapprovalDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.graphics.Bitmap mIcon\nprivate @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.icu.util.ULocale mLocale\nprivate @android.annotation.NonNull java.lang.String mPackageName\nprivate long mBuilderFieldsSet\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.PreapprovalDetails.Builder setIcon(android.graphics.Bitmap)\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.PreapprovalDetails.Builder setLabel(java.lang.CharSequence)\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.PreapprovalDetails.Builder setLocale(android.icu.util.ULocale)\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.PreapprovalDetails.Builder setPackageName(java.lang.String)\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.PreapprovalDetails build()\nprivate void checkNotUsed()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true)") @Deprecated private void __metadata() {} @@ -4347,7 +4324,7 @@ public class PackageInstaller { }; @DataClass.Generated( - time = 1675135664641L, + time = 1676970504336L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/PackageInstaller.java", inputSignatures = "private boolean mAllConstraintsSatisfied\npublic boolean areAllConstraintsSatisfied()\nclass InstallConstraintsResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genHiddenConstructor=true)") @@ -4635,7 +4612,7 @@ public class PackageInstaller { }; @DataClass.Generated( - time = 1675135664653L, + time = 1676970504352L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/PackageInstaller.java", inputSignatures = "public static final @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints GENTLE_UPDATE\nprivate final boolean mDeviceIdleRequired\nprivate final boolean mAppNotForegroundRequired\nprivate final boolean mAppNotInteractingRequired\nprivate final boolean mAppNotTopVisibleRequired\nprivate final boolean mNotInCallRequired\nclass InstallConstraints extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mDeviceIdleRequired\nprivate boolean mAppNotForegroundRequired\nprivate boolean mAppNotInteractingRequired\nprivate boolean mAppNotTopVisibleRequired\nprivate boolean mNotInCallRequired\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints.Builder setDeviceIdleRequired()\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints.Builder setAppNotForegroundRequired()\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints.Builder setAppNotInteractingRequired()\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints.Builder setAppNotTopVisibleRequired()\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints.Builder setNotInCallRequired()\npublic @android.annotation.NonNull android.content.pm.PackageInstaller.InstallConstraints build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genParcelable=true, genHiddenConstructor=true, genEqualsHashCode=true)") diff --git a/core/java/android/hardware/biometrics/AuthenticateOptions.java b/core/java/android/hardware/biometrics/AuthenticateOptions.java new file mode 100644 index 000000000000..77660713275f --- /dev/null +++ b/core/java/android/hardware/biometrics/AuthenticateOptions.java @@ -0,0 +1,77 @@ +/* + * 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 android.hardware.biometrics; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Common authentication options that are exposed across all modalities. + * + * @hide + */ +public interface AuthenticateOptions { + + /** The user id for this operation. */ + int getUserId(); + + /** The sensor id for this operation. */ + int getSensorId(); + + /** The state is unknown. */ + int DISPLAY_STATE_UNKNOWN = 0; + + /** The display is on and showing the lockscreen (or an occluding app). */ + int DISPLAY_STATE_LOCKSCREEN = 1; + + /** The display is off or dozing. */ + int DISPLAY_STATE_NO_UI = 2; + + /** The display is showing a screensaver (dreaming). */ + int DISPLAY_STATE_SCREENSAVER = 3; + + /** The display is dreaming with always on display. */ + int DISPLAY_STATE_AOD = 4; + + /** The doze state of the device. */ + @IntDef(prefix = "DISPLAY_STATE_", value = { + DISPLAY_STATE_UNKNOWN, + DISPLAY_STATE_LOCKSCREEN, + DISPLAY_STATE_NO_UI, + DISPLAY_STATE_SCREENSAVER, + DISPLAY_STATE_AOD + }) + @Retention(RetentionPolicy.SOURCE) + @interface DisplayState {} + + /** The current doze state of the device. */ + @DisplayState + int getDisplayState(); + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + */ + @NonNull String getOpPackageName(); + + /** The attribution tag, if any. */ + @Nullable String getAttributionTag(); +} diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index eeff6ccafd5d..b766cd19cdb0 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -68,6 +68,15 @@ public final class DisplayManager { private static final boolean DEBUG = false; private static final boolean ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE = true; + /** + * The hdr output control feature flag, the value should be read via + * {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)} with + * {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace. + * @hide + */ + @TestApi + public static final String HDR_OUTPUT_CONTROL_FLAG = "enable_hdr_output_control"; + private final Context mContext; private final DisplayManagerGlobal mGlobal; diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.aidl b/core/java/android/hardware/face/FaceAuthenticateOptions.aidl new file mode 100644 index 000000000000..7c72b405383c --- /dev/null +++ b/core/java/android/hardware/face/FaceAuthenticateOptions.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.hardware.face; + +parcelable FaceAuthenticateOptions; diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java new file mode 100644 index 000000000000..4009fa7682fe --- /dev/null +++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java @@ -0,0 +1,624 @@ +/* + * 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 android.hardware.face; + + +import static android.os.PowerManager.WAKE_REASON_UNKNOWN; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.AuthenticateOptions; +import android.os.Parcelable; +import android.os.PowerManager; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Additional options when requesting Face authentication or detection. + * + * @hide + */ +@DataClass( + genParcelable = true, + genAidl = true, + genBuilder = true, + genSetters = true, + genEqualsHashCode = true +) +public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable { + + /** The user id for this operation. */ + private final int mUserId; + private static int defaultUserId() { + return 0; + } + + /** The sensor id for this operation. */ + private final int mSensorId; + private static int defaultSensorId() { + return -1; + } + + /** The current doze state of the device. */ + @AuthenticateOptions.DisplayState + private final int mDisplayState; + private static int defaultDisplayState() { + return DISPLAY_STATE_UNKNOWN; + } + + public static final int AUTHENTICATE_REASON_UNKNOWN = 0; + public static final int AUTHENTICATE_REASON_STARTED_WAKING_UP = 1; + public static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN = 2; + public static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE = 3; + public static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN = 4; + public static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED = 5; + public static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED = 6; + public static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED = 7; + public static final int AUTHENTICATE_REASON_QS_EXPANDED = 8; + public static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER = 9; + public static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN = 10; + + /** + * The reason for this operation when requested by the system (sysui), + * otherwise AUTHENTICATE_REASON_UNKNOWN. + * + * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt + * for more details about each reason. + */ + @AuthenticateReason + private final int mAuthenticateReason; + private static int defaultAuthenticateReason() { + return AUTHENTICATE_REASON_UNKNOWN; + } + + /** A reason if this request was triggered due to a power event or WAKE_REASON_UNKNOWN. */ + @PowerManager.WakeReason + private final int mWakeReason; + private static int defaultWakeReason() { + return WAKE_REASON_UNKNOWN; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @NonNull + private String mOpPackageName; + private static String defaultOpPackageName() { + return ""; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @Nullable + private String mAttributionTag; + private static String defaultAttributionTag() { + return null; + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @IntDef(prefix = "AUTHENTICATE_REASON_", value = { + AUTHENTICATE_REASON_UNKNOWN, + AUTHENTICATE_REASON_STARTED_WAKING_UP, + AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN, + AUTHENTICATE_REASON_ASSISTANT_VISIBLE, + AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN, + AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED, + AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED, + AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED, + AUTHENTICATE_REASON_QS_EXPANDED, + AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER, + AUTHENTICATE_REASON_UDFPS_POINTER_DOWN + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface AuthenticateReason {} + + @DataClass.Generated.Member + public static String authenticateReasonToString(@AuthenticateReason int value) { + switch (value) { + case AUTHENTICATE_REASON_UNKNOWN: + return "AUTHENTICATE_REASON_UNKNOWN"; + case AUTHENTICATE_REASON_STARTED_WAKING_UP: + return "AUTHENTICATE_REASON_STARTED_WAKING_UP"; + case AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN: + return "AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN"; + case AUTHENTICATE_REASON_ASSISTANT_VISIBLE: + return "AUTHENTICATE_REASON_ASSISTANT_VISIBLE"; + case AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN: + return "AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN"; + case AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED: + return "AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED"; + case AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED: + return "AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED"; + case AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED: + return "AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED"; + case AUTHENTICATE_REASON_QS_EXPANDED: + return "AUTHENTICATE_REASON_QS_EXPANDED"; + case AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER: + return "AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER"; + case AUTHENTICATE_REASON_UDFPS_POINTER_DOWN: + return "AUTHENTICATE_REASON_UDFPS_POINTER_DOWN"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + /* package-private */ FaceAuthenticateOptions( + int userId, + int sensorId, + @AuthenticateOptions.DisplayState int displayState, + @AuthenticateReason int authenticateReason, + @PowerManager.WakeReason int wakeReason, + @NonNull String opPackageName, + @Nullable String attributionTag) { + this.mUserId = userId; + this.mSensorId = sensorId; + this.mDisplayState = displayState; + com.android.internal.util.AnnotationValidations.validate( + AuthenticateOptions.DisplayState.class, null, mDisplayState); + this.mAuthenticateReason = authenticateReason; + + if (!(mAuthenticateReason == AUTHENTICATE_REASON_UNKNOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_STARTED_WAKING_UP) + && !(mAuthenticateReason == AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_ASSISTANT_VISIBLE) + && !(mAuthenticateReason == AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_QS_EXPANDED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER) + && !(mAuthenticateReason == AUTHENTICATE_REASON_UDFPS_POINTER_DOWN)) { + throw new java.lang.IllegalArgumentException( + "authenticateReason was " + mAuthenticateReason + " but must be one of: " + + "AUTHENTICATE_REASON_UNKNOWN(" + AUTHENTICATE_REASON_UNKNOWN + "), " + + "AUTHENTICATE_REASON_STARTED_WAKING_UP(" + AUTHENTICATE_REASON_STARTED_WAKING_UP + "), " + + "AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN(" + AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN + "), " + + "AUTHENTICATE_REASON_ASSISTANT_VISIBLE(" + AUTHENTICATE_REASON_ASSISTANT_VISIBLE + "), " + + "AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN(" + AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN + "), " + + "AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED(" + AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED + "), " + + "AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED(" + AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED + "), " + + "AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED(" + AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED + "), " + + "AUTHENTICATE_REASON_QS_EXPANDED(" + AUTHENTICATE_REASON_QS_EXPANDED + "), " + + "AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER(" + AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER + "), " + + "AUTHENTICATE_REASON_UDFPS_POINTER_DOWN(" + AUTHENTICATE_REASON_UDFPS_POINTER_DOWN + ")"); + } + + this.mWakeReason = wakeReason; + com.android.internal.util.AnnotationValidations.validate( + PowerManager.WakeReason.class, null, mWakeReason); + this.mOpPackageName = opPackageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + this.mAttributionTag = attributionTag; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The user id for this operation. + */ + @DataClass.Generated.Member + public int getUserId() { + return mUserId; + } + + /** + * The sensor id for this operation. + */ + @DataClass.Generated.Member + public int getSensorId() { + return mSensorId; + } + + /** + * The current doze state of the device. + */ + @DataClass.Generated.Member + public @AuthenticateOptions.DisplayState int getDisplayState() { + return mDisplayState; + } + + /** + * The reason for this operation when requested by the system (sysui), + * otherwise AUTHENTICATE_REASON_UNKNOWN. + * + * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt + * for more details about each reason. + */ + @DataClass.Generated.Member + public @AuthenticateReason int getAuthenticateReason() { + return mAuthenticateReason; + } + + /** + * A reason if this request was triggered due to a power event or WAKE_REASON_UNKNOWN. + */ + @DataClass.Generated.Member + public @PowerManager.WakeReason int getWakeReason() { + return mWakeReason; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull String getOpPackageName() { + return mOpPackageName; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @Nullable String getAttributionTag() { + return mAttributionTag; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull FaceAuthenticateOptions setOpPackageName(@NonNull String value) { + mOpPackageName = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + return this; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull FaceAuthenticateOptions setAttributionTag(@NonNull String value) { + mAttributionTag = value; + return this; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(FaceAuthenticateOptions other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + FaceAuthenticateOptions that = (FaceAuthenticateOptions) o; + //noinspection PointlessBooleanExpression + return true + && mUserId == that.mUserId + && mSensorId == that.mSensorId + && mDisplayState == that.mDisplayState + && mAuthenticateReason == that.mAuthenticateReason + && mWakeReason == that.mWakeReason + && java.util.Objects.equals(mOpPackageName, that.mOpPackageName) + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mUserId; + _hash = 31 * _hash + mSensorId; + _hash = 31 * _hash + mDisplayState; + _hash = 31 * _hash + mAuthenticateReason; + _hash = 31 * _hash + mWakeReason; + _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mAttributionTag != null) flg |= 0x40; + dest.writeByte(flg); + dest.writeInt(mUserId); + dest.writeInt(mSensorId); + dest.writeInt(mDisplayState); + dest.writeInt(mAuthenticateReason); + dest.writeInt(mWakeReason); + dest.writeString(mOpPackageName); + if (mAttributionTag != null) dest.writeString(mAttributionTag); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected FaceAuthenticateOptions(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int userId = in.readInt(); + int sensorId = in.readInt(); + int displayState = in.readInt(); + int authenticateReason = in.readInt(); + int wakeReason = in.readInt(); + String opPackageName = in.readString(); + String attributionTag = (flg & 0x40) == 0 ? null : in.readString(); + + this.mUserId = userId; + this.mSensorId = sensorId; + this.mDisplayState = displayState; + com.android.internal.util.AnnotationValidations.validate( + AuthenticateOptions.DisplayState.class, null, mDisplayState); + this.mAuthenticateReason = authenticateReason; + + if (!(mAuthenticateReason == AUTHENTICATE_REASON_UNKNOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_STARTED_WAKING_UP) + && !(mAuthenticateReason == AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_ASSISTANT_VISIBLE) + && !(mAuthenticateReason == AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN) + && !(mAuthenticateReason == AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_QS_EXPANDED) + && !(mAuthenticateReason == AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER) + && !(mAuthenticateReason == AUTHENTICATE_REASON_UDFPS_POINTER_DOWN)) { + throw new java.lang.IllegalArgumentException( + "authenticateReason was " + mAuthenticateReason + " but must be one of: " + + "AUTHENTICATE_REASON_UNKNOWN(" + AUTHENTICATE_REASON_UNKNOWN + "), " + + "AUTHENTICATE_REASON_STARTED_WAKING_UP(" + AUTHENTICATE_REASON_STARTED_WAKING_UP + "), " + + "AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN(" + AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN + "), " + + "AUTHENTICATE_REASON_ASSISTANT_VISIBLE(" + AUTHENTICATE_REASON_ASSISTANT_VISIBLE + "), " + + "AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN(" + AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN + "), " + + "AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED(" + AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED + "), " + + "AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED(" + AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED + "), " + + "AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED(" + AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED + "), " + + "AUTHENTICATE_REASON_QS_EXPANDED(" + AUTHENTICATE_REASON_QS_EXPANDED + "), " + + "AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER(" + AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER + "), " + + "AUTHENTICATE_REASON_UDFPS_POINTER_DOWN(" + AUTHENTICATE_REASON_UDFPS_POINTER_DOWN + ")"); + } + + this.mWakeReason = wakeReason; + com.android.internal.util.AnnotationValidations.validate( + PowerManager.WakeReason.class, null, mWakeReason); + this.mOpPackageName = opPackageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + this.mAttributionTag = attributionTag; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<FaceAuthenticateOptions> CREATOR + = new Parcelable.Creator<FaceAuthenticateOptions>() { + @Override + public FaceAuthenticateOptions[] newArray(int size) { + return new FaceAuthenticateOptions[size]; + } + + @Override + public FaceAuthenticateOptions createFromParcel(@NonNull android.os.Parcel in) { + return new FaceAuthenticateOptions(in); + } + }; + + /** + * A builder for {@link FaceAuthenticateOptions} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder { + + private int mUserId; + private int mSensorId; + private @AuthenticateOptions.DisplayState int mDisplayState; + private @AuthenticateReason int mAuthenticateReason; + private @PowerManager.WakeReason int mWakeReason; + private @NonNull String mOpPackageName; + private @Nullable String mAttributionTag; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * The user id for this operation. + */ + @DataClass.Generated.Member + public @NonNull Builder setUserId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mUserId = value; + return this; + } + + /** + * The sensor id for this operation. + */ + @DataClass.Generated.Member + public @NonNull Builder setSensorId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mSensorId = value; + return this; + } + + /** + * The current doze state of the device. + */ + @DataClass.Generated.Member + public @NonNull Builder setDisplayState(@AuthenticateOptions.DisplayState int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mDisplayState = value; + return this; + } + + /** + * The reason for this operation when requested by the system (sysui), + * otherwise AUTHENTICATE_REASON_UNKNOWN. + * + * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt + * for more details about each reason. + */ + @DataClass.Generated.Member + public @NonNull Builder setAuthenticateReason(@AuthenticateReason int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mAuthenticateReason = value; + return this; + } + + /** + * A reason if this request was triggered due to a power event or WAKE_REASON_UNKNOWN. + */ + @DataClass.Generated.Member + public @NonNull Builder setWakeReason(@PowerManager.WakeReason int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mWakeReason = value; + return this; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull Builder setOpPackageName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mOpPackageName = value; + return this; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull Builder setAttributionTag(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mAttributionTag = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull FaceAuthenticateOptions build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mUserId = defaultUserId(); + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mSensorId = defaultSensorId(); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mDisplayState = defaultDisplayState(); + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mAuthenticateReason = defaultAuthenticateReason(); + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mWakeReason = defaultWakeReason(); + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mOpPackageName = defaultOpPackageName(); + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mAttributionTag = defaultAttributionTag(); + } + FaceAuthenticateOptions o = new FaceAuthenticateOptions( + mUserId, + mSensorId, + mDisplayState, + mAuthenticateReason, + mWakeReason, + mOpPackageName, + mAttributionTag); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x80) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1676508211385L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java", + inputSignatures = "private final int mUserId\nprivate final int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 96098f853552..55b20e17d4d7 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -194,18 +194,30 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Request authentication of a crypto object. This call operates the face recognition hardware - * and starts capturing images. It terminates when + * @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FaceAuthenticateOptions)}. + */ + @Deprecated + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, + @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId) { + authenticate(crypto, cancel, callback, handler, new FaceAuthenticateOptions.Builder() + .setUserId(userId) + .build()); + } + + /** + * Request authentication. This call operates the face recognition hardware and starts capturing images. + * It terminates when * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. * - * @param crypto object associated with the call or null if none required. + * @param crypto object associated with the call or null if none required * @param cancel an object that can be used to cancel authentication * @param callback an object to receive authentication events * @param handler an optional handler to handle callback events - * @param userId userId to authenticate for + * @param options additional options to customize this request * @throws IllegalArgumentException if the crypto operation is not supported or is not backed * by * <a href="{@docRoot}training/articles/keystore.html">Android @@ -215,8 +227,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId, - boolean isKeyguardBypassEnabled) { + @NonNull AuthenticationCallback callback, @Nullable Handler handler, + @NonNull FaceAuthenticateOptions options) { if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -226,6 +238,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return; } + options.setOpPackageName(mContext.getOpPackageName()); + options.setAttributionTag(mContext.getAttributionTag()); + if (mService != null) { try { useHandler(handler); @@ -233,8 +248,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; Trace.beginSection("FaceManager#authenticate"); - final long authId = mService.authenticate(mToken, operationId, userId, - mServiceReceiver, mContext.getOpPackageName(), isKeyguardBypassEnabled); + final long authId = mService.authenticate( + mToken, operationId, mServiceReceiver, options); if (cancel != null) { cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); } @@ -258,7 +273,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void detectFace(@NonNull CancellationSignal cancel, - @NonNull FaceDetectionCallback callback, int userId) { + @NonNull FaceDetectionCallback callback, @NonNull FaceAuthenticateOptions options) { if (mService == null) { return; } @@ -268,11 +283,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return; } + options.setOpPackageName(mContext.getOpPackageName()); + options.setAttributionTag(mContext.getAttributionTag()); + mFaceDetectionCallback = callback; try { - final long authId = mService.detectFace( - mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + final long authId = mService.detectFace(mToken, mServiceReceiver, options); cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 5fcc31e3ea25..2857627bf712 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -24,6 +24,7 @@ import android.hardware.biometrics.ITestSessionCallback; import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorPropertiesInternal; import android.view.Surface; @@ -52,14 +53,14 @@ interface IFaceService { // Authenticate with a face. A requestId is returned that can be used to cancel this operation. @EnforcePermission("USE_BIOMETRIC_INTERNAL") - long authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver, - String opPackageName, boolean isKeyguardBypassEnabled); + long authenticate(IBinder token, long operationId, IFaceServiceReceiver receiver, + in FaceAuthenticateOptions options); // Uses the face hardware to detect for the presence of a face, without giving details // about accept/reject/lockout. A requestId is returned that can be used to cancel this // operation. @EnforcePermission("USE_BIOMETRIC_INTERNAL") - long detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName); + long detectFace(IBinder token, IFaceServiceReceiver receiver, in FaceAuthenticateOptions options); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be @@ -68,8 +69,8 @@ interface IFaceService { // startPreparedClient(). @EnforcePermission("USE_BIOMETRIC_INTERNAL") void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, - long operationId, int userId, IBiometricSensorReceiver sensorReceiver, - String opPackageName, long requestId, int cookie, + long operationId, IBiometricSensorReceiver sensorReceiver, + in FaceAuthenticateOptions options, long requestId, int cookie, boolean allowBackgroundAuthentication); // Starts authentication with the previously prepared client. diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.aidl b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.aidl new file mode 100644 index 000000000000..ad30fab89a3b --- /dev/null +++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.hardware.fingerprint; + +parcelable FingerprintAuthenticateOptions; diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java new file mode 100644 index 000000000000..cecb3172a8d1 --- /dev/null +++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java @@ -0,0 +1,447 @@ +/* + * 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 android.hardware.fingerprint; + +import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.AuthenticateOptions; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Additional options when requesting Fingerprint authentication or detection. + * + * @hide + */ +@DataClass( + genParcelable = true, + genAidl = true, + genBuilder = true, + genSetters = true, + genEqualsHashCode = true +) +public final class FingerprintAuthenticateOptions implements AuthenticateOptions, Parcelable { + + /** The user id for this operation. */ + private final int mUserId; + private static int defaultUserId() { + return 0; + } + + /** The sensor id for this operation. */ + private final int mSensorId; + private static int defaultSensorId() { + return SENSOR_ID_ANY; + } + + /** If enrollment state should be ignored. */ + private final boolean mIgnoreEnrollmentState; + private static boolean defaultIgnoreEnrollmentState() { + return false; + } + + /** The current doze state of the device. */ + @AuthenticateOptions.DisplayState + private final int mDisplayState; + private static int defaultDisplayState() { + return DISPLAY_STATE_UNKNOWN; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @NonNull private String mOpPackageName; + private static String defaultOpPackageName() { + return ""; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @Nullable private String mAttributionTag; + private static String defaultAttributionTag() { + return null; + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ FingerprintAuthenticateOptions( + int userId, + int sensorId, + boolean ignoreEnrollmentState, + @AuthenticateOptions.DisplayState int displayState, + @NonNull String opPackageName, + @Nullable String attributionTag) { + this.mUserId = userId; + this.mSensorId = sensorId; + this.mIgnoreEnrollmentState = ignoreEnrollmentState; + this.mDisplayState = displayState; + com.android.internal.util.AnnotationValidations.validate( + AuthenticateOptions.DisplayState.class, null, mDisplayState); + this.mOpPackageName = opPackageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + this.mAttributionTag = attributionTag; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The user id for this operation. + */ + @DataClass.Generated.Member + public int getUserId() { + return mUserId; + } + + /** + * The sensor id for this operation. + */ + @DataClass.Generated.Member + public int getSensorId() { + return mSensorId; + } + + /** + * If enrollment state should be ignored. + */ + @DataClass.Generated.Member + public boolean isIgnoreEnrollmentState() { + return mIgnoreEnrollmentState; + } + + /** + * The current doze state of the device. + */ + @DataClass.Generated.Member + public @AuthenticateOptions.DisplayState int getDisplayState() { + return mDisplayState; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull String getOpPackageName() { + return mOpPackageName; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @Nullable String getAttributionTag() { + return mAttributionTag; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull FingerprintAuthenticateOptions setOpPackageName(@NonNull String value) { + mOpPackageName = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + return this; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull FingerprintAuthenticateOptions setAttributionTag(@NonNull String value) { + mAttributionTag = value; + return this; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(FingerprintAuthenticateOptions other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + FingerprintAuthenticateOptions that = (FingerprintAuthenticateOptions) o; + //noinspection PointlessBooleanExpression + return true + && mUserId == that.mUserId + && mSensorId == that.mSensorId + && mIgnoreEnrollmentState == that.mIgnoreEnrollmentState + && mDisplayState == that.mDisplayState + && java.util.Objects.equals(mOpPackageName, that.mOpPackageName) + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mUserId; + _hash = 31 * _hash + mSensorId; + _hash = 31 * _hash + Boolean.hashCode(mIgnoreEnrollmentState); + _hash = 31 * _hash + mDisplayState; + _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mIgnoreEnrollmentState) flg |= 0x4; + if (mAttributionTag != null) flg |= 0x20; + dest.writeByte(flg); + dest.writeInt(mUserId); + dest.writeInt(mSensorId); + dest.writeInt(mDisplayState); + dest.writeString(mOpPackageName); + if (mAttributionTag != null) dest.writeString(mAttributionTag); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ FingerprintAuthenticateOptions(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean ignoreEnrollmentState = (flg & 0x4) != 0; + int userId = in.readInt(); + int sensorId = in.readInt(); + int displayState = in.readInt(); + String opPackageName = in.readString(); + String attributionTag = (flg & 0x20) == 0 ? null : in.readString(); + + this.mUserId = userId; + this.mSensorId = sensorId; + this.mIgnoreEnrollmentState = ignoreEnrollmentState; + this.mDisplayState = displayState; + com.android.internal.util.AnnotationValidations.validate( + AuthenticateOptions.DisplayState.class, null, mDisplayState); + this.mOpPackageName = opPackageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mOpPackageName); + this.mAttributionTag = attributionTag; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<FingerprintAuthenticateOptions> CREATOR + = new Parcelable.Creator<FingerprintAuthenticateOptions>() { + @Override + public FingerprintAuthenticateOptions[] newArray(int size) { + return new FingerprintAuthenticateOptions[size]; + } + + @Override + public FingerprintAuthenticateOptions createFromParcel(@NonNull android.os.Parcel in) { + return new FingerprintAuthenticateOptions(in); + } + }; + + /** + * A builder for {@link FingerprintAuthenticateOptions} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private int mUserId; + private int mSensorId; + private boolean mIgnoreEnrollmentState; + private @AuthenticateOptions.DisplayState int mDisplayState; + private @NonNull String mOpPackageName; + private @Nullable String mAttributionTag; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * The user id for this operation. + */ + @DataClass.Generated.Member + public @NonNull Builder setUserId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mUserId = value; + return this; + } + + /** + * The sensor id for this operation. + */ + @DataClass.Generated.Member + public @NonNull Builder setSensorId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mSensorId = value; + return this; + } + + /** + * If enrollment state should be ignored. + */ + @DataClass.Generated.Member + public @NonNull Builder setIgnoreEnrollmentState(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mIgnoreEnrollmentState = value; + return this; + } + + /** + * The current doze state of the device. + */ + @DataClass.Generated.Member + public @NonNull Builder setDisplayState(@AuthenticateOptions.DisplayState int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mDisplayState = value; + return this; + } + + /** + * The package name for that operation that should be used for + * {@link android.app.AppOpsManager} verification. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull Builder setOpPackageName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mOpPackageName = value; + return this; + } + + /** + * The attribution tag, if any. + * + * This option may be overridden by the FingerprintManager using the caller's context. + */ + @DataClass.Generated.Member + public @NonNull Builder setAttributionTag(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mAttributionTag = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull FingerprintAuthenticateOptions build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mUserId = defaultUserId(); + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mSensorId = defaultSensorId(); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mIgnoreEnrollmentState = defaultIgnoreEnrollmentState(); + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mDisplayState = defaultDisplayState(); + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mOpPackageName = defaultOpPackageName(); + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mAttributionTag = defaultAttributionTag(); + } + FingerprintAuthenticateOptions o = new FingerprintAuthenticateOptions( + mUserId, + mSensorId, + mIgnoreEnrollmentState, + mDisplayState, + mOpPackageName, + mAttributionTag); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x40) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1676508212083L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java", + inputSignatures = "private final int mUserId\nprivate final int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d9f8f8e0e715..eb8136e39d29 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -577,8 +577,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * Per-user version of authenticate. + * @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}. * @hide */ + @Deprecated @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback, Handler handler, int userId) { @@ -587,13 +589,29 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * Per-user and per-sensor version of authenticate. + * @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}. * @hide */ + @Deprecated @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId, int flags) { + authenticate(crypto, cancel, callback, handler, new FingerprintAuthenticateOptions.Builder() + .setSensorId(sensorId) + .setUserId(userId) + .setIgnoreEnrollmentState(flags != 0) + .build()); + } + /** + * Version of authenticate with additional options. + * @hide + */ + @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) + public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, + @NonNull AuthenticationCallback callback, @NonNull Handler handler, + @NonNull FingerprintAuthenticateOptions options) { FrameworkStatsLog.write(FrameworkStatsLog.AUTH_DEPRECATED_API_USED, AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE, mContext.getApplicationInfo().uid, @@ -608,7 +626,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return; } - final boolean ignoreEnrollmentState = flags == 0 ? false : true; + options.setOpPackageName(mContext.getOpPackageName()); + options.setAttributionTag(mContext.getAttributionTag()); if (mService != null) { try { @@ -616,16 +635,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mAuthenticationCallback = callback; mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; - final long authId = - mService.authenticate( - mToken, - operationId, - sensorId, - userId, - mServiceReceiver, - mContext.getOpPackageName(), - mContext.getAttributionTag(), - ignoreEnrollmentState); + final long authId = mService.authenticate(mToken, operationId, mServiceReceiver, options); if (cancel != null) { cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); } @@ -647,7 +657,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void detectFingerprint(@NonNull CancellationSignal cancel, - @NonNull FingerprintDetectionCallback callback, int userId) { + @NonNull FingerprintDetectionCallback callback, @NonNull FingerprintAuthenticateOptions options) { if (mService == null) { return; } @@ -657,11 +667,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return; } + options.setOpPackageName(mContext.getOpPackageName()); + options.setAttributionTag(mContext.getAttributionTag()); + mFingerprintDetectionCallback = callback; try { - final long authId = mService.detectFingerprint(mToken, userId, mServiceReceiver, - mContext.getOpPackageName()); + final long authId = mService.detectFingerprint(mToken, mServiceReceiver, options); cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 6f35713d434f..e3ae299be730 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -29,6 +29,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlay; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import java.util.List; @@ -56,16 +57,15 @@ interface IFingerprintService { // Authenticate with a fingerprint. This is protected by USE_FINGERPRINT/USE_BIOMETRIC // permission. This is effectively deprecated, since it only comes through FingerprintManager // now. A requestId is returned that can be used to cancel this operation. - long authenticate(IBinder token, long operationId, int sensorId, int userId, - IFingerprintServiceReceiver receiver, String opPackageName, String attributionTag, - boolean shouldIgnoreEnrollmentState); + long authenticate(IBinder token, long operationId, IFingerprintServiceReceiver receiver, + in FingerprintAuthenticateOptions options); // Uses the fingerprint hardware to detect for the presence of a finger, without giving details // about accept/reject/lockout. A requestId is returned that can be used to cancel this // operation. @EnforcePermission("USE_BIOMETRIC_INTERNAL") - long detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver, - String opPackageName); + long detectFingerprint(IBinder token, IFingerprintServiceReceiver receiver, + in FingerprintAuthenticateOptions options); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 66442bb9e2c9..1a5613ebef02 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1352,7 +1352,6 @@ public class KeyEvent extends InputEvent implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mSource; private int mDisplayId = INVALID_DISPLAY; - // NOTE: mHmac is private and not used in this class, but it's used on native side / parcel. private @Nullable byte[] mHmac; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mMetaState; @@ -1378,7 +1377,7 @@ public class KeyEvent extends InputEvent implements Parcelable { */ private long mEventTime; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private @Nullable String mCharacters; + private String mCharacters; public interface Callback { /** @@ -1442,11 +1441,7 @@ public class KeyEvent extends InputEvent implements Parcelable { private static native int nativeKeyCodeFromString(String keyCode); private static native int nativeNextId(); - private KeyEvent() { - this(/* downTime= */ 0, /* eventTime= */ 0, /* action= */ 0, /* code= */0, /* repeat= */ 0, - /* metaState= */ 0, /* deviceId= */ 0, /* scancode= */ 0, /* flags= */ 0, - /* source= */ 0); - } + private KeyEvent() {} /** * Create a new key event. @@ -1456,9 +1451,11 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param code The key code. */ public KeyEvent(int action, int code) { - this(/* downTime= */ 0, /* eventTime= */ 0, action, code, /* repeat= */ 0, - /* metaState= */ 0, /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD, - /* scancode= */ 0, /* flags= */ 0, /* source= */ 0); + mId = nativeNextId(); + mAction = action; + mKeyCode = code; + mRepeatCount = 0; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1476,9 +1473,13 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public KeyEvent(long downTime, long eventTime, int action, int code, int repeat) { - this(downTime, eventTime, action, code, repeat, /* metaState= */ 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, /* scancode= */ 0, /* flags= */ 0, - /* source= */ 0); + mId = nativeNextId(); + mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS); + mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); + mAction = action; + mKeyCode = code; + mRepeatCount = repeat; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1497,8 +1498,14 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState) { - this(downTime, eventTime, action, code, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, - /* scancode= */ 0, /* flags= */ 0, /* source= */ 0); + mId = nativeNextId(); + mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS); + mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); + mAction = action; + mKeyCode = code; + mRepeatCount = repeat; + mMetaState = metaState; + mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; } /** @@ -1520,8 +1527,15 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode) { - this(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, - /* flags= */ 0, /* source= */ 0); + mId = nativeNextId(); + mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS); + mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); + mAction = action; + mKeyCode = code; + mRepeatCount = repeat; + mMetaState = metaState; + mDeviceId = deviceId; + mScanCode = scancode; } /** @@ -1544,8 +1558,16 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags) { - this(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags, - /* source= */ 0); + mId = nativeNextId(); + mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS); + mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); + mAction = action; + mKeyCode = code; + mRepeatCount = repeat; + mMetaState = metaState; + mDeviceId = deviceId; + mScanCode = scancode; + mFlags = flags; } /** @@ -1569,8 +1591,6 @@ public class KeyEvent extends InputEvent implements Parcelable { public KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState, int deviceId, int scancode, int flags, int source) { - // NOTE: this is the canonical constructor, every other constructor that takes KeyEvent - // attributes should call it mId = nativeNextId(); mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS); mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); @@ -1597,18 +1617,36 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param flags The flags for this key event */ public KeyEvent(long time, String characters, int deviceId, int flags) { - this(/* downTime= */ time, /* eventTime= */ time, ACTION_MULTIPLE, KEYCODE_UNKNOWN, - /* repeat= */ 0, /* metaState= */ 0, deviceId, /* scancode= */ 0, flags, - /* source= */ InputDevice.SOURCE_KEYBOARD); + mId = nativeNextId(); + mDownTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS); + mEventTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS); + mCharacters = characters; + mAction = ACTION_MULTIPLE; + mKeyCode = KEYCODE_UNKNOWN; + mRepeatCount = 0; + mDeviceId = deviceId; + mFlags = flags; + mSource = InputDevice.SOURCE_KEYBOARD; } /** * Make an exact copy of an existing key event. */ public KeyEvent(KeyEvent origEvent) { - this(origEvent, origEvent.mId, origEvent.mEventTime, origEvent.mAction, - origEvent.mRepeatCount, origEvent.mHmac == null ? null : origEvent.mHmac.clone(), - origEvent.mCharacters); + mId = origEvent.mId; + mDownTime = origEvent.mDownTime; + mEventTime = origEvent.mEventTime; + mAction = origEvent.mAction; + mKeyCode = origEvent.mKeyCode; + mRepeatCount = origEvent.mRepeatCount; + mMetaState = origEvent.mMetaState; + mDeviceId = origEvent.mDeviceId; + mSource = origEvent.mSource; + mDisplayId = origEvent.mDisplayId; + mHmac = origEvent.mHmac == null ? null : origEvent.mHmac.clone(); + mScanCode = origEvent.mScanCode; + mFlags = origEvent.mFlags; + mCharacters = origEvent.mCharacters; } /** @@ -1624,30 +1662,20 @@ public class KeyEvent extends InputEvent implements Parcelable { */ @Deprecated public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) { - // Not an exact copy so assign a new ID. - // Don't copy HMAC, it will be invalid because eventTime is changing - this(origEvent, nativeNextId(), - TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS), origEvent.mAction, - newRepeat, /* hmac= */ null, origEvent.mCharacters); - } - - // This is the canonical constructor that should be called for constructors that take a KeyEvent - private KeyEvent(KeyEvent origEvent, int id, long eventTime, int action, int newRepeat, - @Nullable byte[] hmac, @Nullable String characters) { - mId = id; + mId = nativeNextId(); // Not an exact copy so assign a new ID. mDownTime = origEvent.mDownTime; - mEventTime = eventTime; - mAction = action; + mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS); + mAction = origEvent.mAction; mKeyCode = origEvent.mKeyCode; mRepeatCount = newRepeat; mMetaState = origEvent.mMetaState; mDeviceId = origEvent.mDeviceId; mSource = origEvent.mSource; mDisplayId = origEvent.mDisplayId; - mHmac = hmac; + mHmac = null; // Don't copy HMAC, it will be invalid because eventTime is changing mScanCode = origEvent.mScanCode; mFlags = origEvent.mFlags; - mCharacters = characters; + mCharacters = origEvent.mCharacters; } private static KeyEvent obtain() { @@ -1829,11 +1857,21 @@ public class KeyEvent extends InputEvent implements Parcelable { * @param action The new action code of the event. */ private KeyEvent(KeyEvent origEvent, int action) { - // Not an exact copy so assign a new ID - // Don't copy the hmac, it will be invalid since action is changing - // Don't copy mCharacters, since one way or the other we'll lose it when changing action. - this(origEvent, nativeNextId(), origEvent.mEventTime, action, origEvent.mRepeatCount, - /* hmac= */ null, /* characters= */ null); + mId = nativeNextId(); // Not an exact copy so assign a new ID. + mDownTime = origEvent.mDownTime; + mEventTime = origEvent.mEventTime; + mAction = action; + mKeyCode = origEvent.mKeyCode; + mRepeatCount = origEvent.mRepeatCount; + mMetaState = origEvent.mMetaState; + mDeviceId = origEvent.mDeviceId; + mSource = origEvent.mSource; + mDisplayId = origEvent.mDisplayId; + mHmac = null; // Don't copy the hmac, it will be invalid since action is changing + mScanCode = origEvent.mScanCode; + mFlags = origEvent.mFlags; + // Don't copy mCharacters, since one way or the other we'll lose it + // when changing the action. } /** @@ -3181,8 +3219,6 @@ public class KeyEvent extends InputEvent implements Parcelable { } private KeyEvent(Parcel in) { - // NOTE: ideally this constructor should call the canonical one, but that would require - // changing the order the fields are written to the parcel, which could break native code mId = in.readInt(); mDeviceId = in.readInt(); mSource = in.readInt(); diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 59b5286f6fc5..34c7b8b9889f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -82,11 +82,19 @@ public final class ContentCaptureContext implements Parcelable { @SystemApi public static final int FLAG_RECONNECTED = 0x4; + /** + * Flag used to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final int FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING = 1 << 3; + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_DISABLED_BY_APP, FLAG_DISABLED_BY_FLAG_SECURE, - FLAG_RECONNECTED + FLAG_RECONNECTED, + FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING }) @Retention(RetentionPolicy.SOURCE) @interface ContextCreationFlags{} @@ -252,7 +260,8 @@ public final class ContentCaptureContext implements Parcelable { * Gets the flags associated with this context. * * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE}, - * {@link #FLAG_DISABLED_BY_APP} and {@link #FLAG_RECONNECTED}. + * {@link #FLAG_DISABLED_BY_APP}, {@link #FLAG_RECONNECTED} and {@link + * #FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING}. * * @hide */ diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 497f0668107f..668351b949c1 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -51,6 +51,7 @@ import android.view.WindowManager; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; @@ -343,6 +344,14 @@ public final class ContentCaptureManager { */ public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout"; + /** + * Sets to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = + "disable_flush_for_view_tree_appearing"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -373,6 +382,8 @@ public final class ContentCaptureManager { public static final int DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS = 1_000; /** @hide */ public static final int DEFAULT_LOG_HISTORY_SIZE = 10; + /** @hide */ + public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false; private final Object mLock = new Object(); @@ -448,6 +459,7 @@ public final class ContentCaptureManager { mOptions = Objects.requireNonNull(options, "options cannot be null"); ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel); + setFlushViewTreeAppearingEventDisabled(mOptions.disableFlushForViewTreeAppearing); if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); @@ -687,6 +699,38 @@ public final class ContentCaptureManager { } /** + * Explicitly sets enable or disable flush for view tree appearing event. + * + * @hide + */ + @VisibleForTesting + public void setFlushViewTreeAppearingEventDisabled(boolean disabled) { + if (sDebug) { + Log.d(TAG, "setFlushViewTreeAppearingEventDisabled(): setting to " + disabled); + } + + synchronized (mLock) { + if (disabled) { + mFlags |= ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } else { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } + } + } + + /** + * Gets whether content capture is needed to flush for view tree appearing event. + * + * @hide + */ + public boolean getFlushViewTreeAppearingEventDisabled() { + synchronized (mLock) { + return (mFlags & ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING) + != 0; + } + } + + /** * Gets whether content capture is enabled for the given user. * * <p>This method is typically used by the content capture service settings page, so it can diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 0da7e3b95863..b7f03e1d6d72 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -177,6 +177,10 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int FLUSH_REASON_SESSION_CONNECTED = 7; /** @hide */ public static final int FLUSH_REASON_FORCE_FLUSH = 8; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARING = 9; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARED = 10; @ChangeId @EnabledSince(targetSdkVersion = UPSIDE_DOWN_CAKE) @@ -191,7 +195,9 @@ public abstract class ContentCaptureSession implements AutoCloseable { FLUSH_REASON_IDLE_TIMEOUT, FLUSH_REASON_TEXT_CHANGE_TIMEOUT, FLUSH_REASON_SESSION_CONNECTED, - FLUSH_REASON_FORCE_FLUSH + FLUSH_REASON_FORCE_FLUSH, + FLUSH_REASON_VIEW_TREE_APPEARING, + FLUSH_REASON_VIEW_TREE_APPEARED }) @Retention(RetentionPolicy.SOURCE) public @interface FlushReason{} @@ -671,6 +677,10 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "CONNECTED"; case FLUSH_REASON_FORCE_FLUSH: return "FORCE_FLUSH"; + case FLUSH_REASON_VIEW_TREE_APPEARING: + return "VIEW_TREE_APPEARING"; + case FLUSH_REASON_VIEW_TREE_APPEARED: + return "VIEW_TREE_APPEARED"; default: return "UNKOWN-" + reason; } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 8c040e482cdf..6ddfcb825acc 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -458,6 +458,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { case ContentCaptureEvent.TYPE_SESSION_FINISHED: flushReason = FLUSH_REASON_SESSION_FINISHED; break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARING; + break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARED; + break; default: flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL; } @@ -774,7 +780,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { /** Public because is also used by ViewRootImpl */ public void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; - mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH)); + final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled(); + + mHandler.post(() -> sendEvent( + new ContentCaptureEvent(sessionId, type), + disableFlush ? !started : FORCE_FLUSH)); } void notifySessionResumed(int sessionId) { diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 445e9ecff54f..b5b6f5ae8af8 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -478,7 +478,7 @@ public final class TextLinks implements Parcelable { /** * Sets the entity configuration to use. This determines what types of entities the * TextClassifier will look for. - * Set to {@code null} for the default entity config and teh TextClassifier will + * Set to {@code null} for the default entity config and the TextClassifier will * automatically determine what links to generate. * * @return this builder diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a33906267736..18874f768929 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -6717,6 +6717,10 @@ public class RemoteViews implements Parcelable, Filter { Log.w(LOG_TAG, "getLaunchOptions: view.getDisplay() is null!", new Exception()); } + // If the user interacts with a visible element it is safe to assume they consent that + // something is going to start. + opts.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); return Pair.create(intent, opts); } } diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java new file mode 100644 index 000000000000..9b1f0cd9e8da --- /dev/null +++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java @@ -0,0 +1,151 @@ +/* + * 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 android.hardware.face; + +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.res.Resources; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class FaceManagerTest { + private static final int USER_ID = 4; + private static final String PACKAGE_NAME = "f.m.test"; + private static final String ATTRIBUTION_TAG = "blue"; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private IFaceService mService; + @Mock + private FaceManager.AuthenticationCallback mAuthCallback; + + @Captor + private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor; + @Captor + private ArgumentCaptor<FaceAuthenticateOptions> mOptionsCaptor; + + private List<FaceSensorPropertiesInternal> mProps; + private TestLooper mLooper; + private Handler mHandler; + private FaceManager mFaceManager; + + @Before + public void setUp() throws Exception { + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); + + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + when(mContext.getOpPackageName()).thenReturn(PACKAGE_NAME); + when(mContext.getAttributionTag()).thenReturn(ATTRIBUTION_TAG); + when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getString(anyInt())).thenReturn("string"); + + mFaceManager = new FaceManager(mContext, mService); + mProps = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + FaceSensorProperties.STRENGTH_STRONG, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + } + + @Test + public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException { + verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture()); + + mCaptor.getValue().onAllAuthenticatorsRegistered(mProps); + List<FaceSensorPropertiesInternal> actual = mFaceManager.getSensorPropertiesInternal(); + + assertThat(actual).isEqualTo(mProps); + verify(mService, never()).getSensorPropertiesInternal(any()); + } + + @Test + public void authenticate_withOptions() throws Exception { + mFaceManager.authenticate(null, new CancellationSignal(), mAuthCallback, mHandler, + new FaceAuthenticateOptions.Builder() + .setUserId(USER_ID) + .setOpPackageName("some.thing") + .setAttributionTag(null) + .build()); + + verify(mService).authenticate(any(IBinder.class), eq(0L), + any(IFaceServiceReceiver.class), mOptionsCaptor.capture()); + + assertThat(mOptionsCaptor.getValue()).isEqualTo( + new FaceAuthenticateOptions.Builder() + .setUserId(USER_ID) + .setOpPackageName(PACKAGE_NAME) + .setAttributionTag(ATTRIBUTION_TAG) + .build() + ); + } + + @Test + public void authenticate_errorWhenUnavailable() throws Exception { + when(mService.authenticate(any(), anyLong(), any(), any())) + .thenThrow(new RemoteException()); + + mFaceManager.authenticate(null, new CancellationSignal(), + mAuthCallback, mHandler, + new FaceAuthenticateOptions.Builder().build()); + + verify(mAuthCallback).onAuthenticationError(eq(FACE_ERROR_HW_UNAVAILABLE), any()); + } +} diff --git a/core/tests/coretests/src/android/hardware/face/OWNERS b/core/tests/coretests/src/android/hardware/face/OWNERS new file mode 100644 index 000000000000..6a2192a2c7fb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/face/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java new file mode 100644 index 000000000000..f31903a73111 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java @@ -0,0 +1,152 @@ +/* + * 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 android.hardware.fingerprint; + +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.res.Resources; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class FingerprintManagerTest { + private static final int USER_ID = 9; + private static final String PACKAGE_NAME = "finger.food.test"; + private static final String ATTRIBUTION_TAG = "taz"; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private IFingerprintService mService; + @Mock + private FingerprintManager.AuthenticationCallback mAuthCallback; + + @Captor + private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor; + @Captor + private ArgumentCaptor<FingerprintAuthenticateOptions> mOptionsCaptor; + + private List<FingerprintSensorPropertiesInternal> mProps; + private TestLooper mLooper; + private Handler mHandler; + private FingerprintManager mFingerprintManager; + + @Before + public void setUp() throws Exception { + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); + + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + when(mContext.getOpPackageName()).thenReturn(PACKAGE_NAME); + when(mContext.getAttributionTag()).thenReturn(ATTRIBUTION_TAG); + when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getString(anyInt())).thenReturn("string"); + + mFingerprintManager = new FingerprintManager(mContext, mService); + mProps = List.of(new FingerprintSensorPropertiesInternal( + 0 /* sensorId */, + FingerprintSensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + new ArrayList<>() /* componentInfo */, + FingerprintSensorProperties.TYPE_UNKNOWN, + true /* halControlsIllumination */, + true /* resetLockoutRequiresHardwareAuthToken */, + new ArrayList<>() /* sensorLocations */)); + } + + @Test + public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException { + verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture()); + + mCaptor.getValue().onAllAuthenticatorsRegistered(mProps); + List<FingerprintSensorPropertiesInternal> actual = + mFingerprintManager.getSensorPropertiesInternal(); + + assertThat(actual).isEqualTo(mProps); + verify(mService, never()).getSensorPropertiesInternal(any()); + } + + @Test + public void authenticate_withOptions() throws Exception { + mFingerprintManager.authenticate(null, new CancellationSignal(), mAuthCallback, mHandler, + new FingerprintAuthenticateOptions.Builder() + .setUserId(USER_ID) + .setOpPackageName("some.thing") + .setAttributionTag(null) + .build()); + + verify(mService).authenticate(any(IBinder.class), eq(0L), + any(IFingerprintServiceReceiver.class), mOptionsCaptor.capture()); + + assertThat(mOptionsCaptor.getValue()).isEqualTo( + new FingerprintAuthenticateOptions.Builder() + .setUserId(USER_ID) + .setOpPackageName(PACKAGE_NAME) + .setAttributionTag(ATTRIBUTION_TAG) + .build() + ); + } + + @Test + public void authenticate_errorWhenUnavailable() throws Exception { + when(mService.authenticate(any(), anyLong(), any(), any())) + .thenThrow(new RemoteException()); + + mFingerprintManager.authenticate(null, new CancellationSignal(), + mAuthCallback, mHandler, + new FingerprintAuthenticateOptions.Builder().build()); + + verify(mAuthCallback).onAuthenticationError(eq(FINGERPRINT_ERROR_HW_UNAVAILABLE), any()); + } +} diff --git a/core/tests/coretests/src/android/hardware/fingerprint/OWNERS b/core/tests/coretests/src/android/hardware/fingerprint/OWNERS new file mode 100644 index 000000000000..6a2192a2c7fb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/fingerprint/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index eae1bbc930d4..17ed4c478350 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.testng.Assert.assertThrows; @@ -54,4 +56,19 @@ public class ContentCaptureManagerTest { assertThrows(NullPointerException.class, () -> manager.removeData(null)); } + + @Test + @SuppressWarnings("GuardedBy") + public void testFlushViewTreeAppearingEventDisabled_setAndGet() { + final IContentCaptureManager mockService = mock(IContentCaptureManager.class); + final ContentCaptureOptions options = new ContentCaptureOptions(null); + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mockService, options); + + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + manager.setFlushViewTreeAppearingEventDisabled(true); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isTrue(); + manager.setFlushViewTreeAppearingEventDisabled(false); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + } } diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java index 470a06c1f1f5..a7c508c75ac9 100644 --- a/graphics/java/android/graphics/Gainmap.java +++ b/graphics/java/android/graphics/Gainmap.java @@ -96,8 +96,8 @@ public final class Gainmap implements Parcelable { throw new RuntimeException("internal error: native gainmap is 0"); } - mGainmapContents = gainmapContents; mNativePtr = nativeGainmap; + setGainmapContents(gainmapContents); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, nativeGainmap); } diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 0f4521951e3d..c7c94246b96a 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -49,6 +49,7 @@ filegroup { "src/com/android/wm/shell/animation/Interpolators.java", "src/com/android/wm/shell/pip/PipContentOverlay.java", "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java", + "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java", ], path: "src", } diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml deleted file mode 100644 index 0d8811357c05..000000000000 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="@color/letterbox_education_accent_primary"/> - <corners android:radius="12dp"/> -</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml index 42572d64b96f..a2699681e656 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml @@ -14,7 +14,30 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/letterbox_education_dismiss_button_background_ripple"> - <item android:drawable="@drawable/letterbox_education_dismiss_button_background"/> -</ripple>
\ No newline at end of file +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:insetTop="@dimen/letterbox_education_dialog_vertical_inset" + android:insetBottom="@dimen/letterbox_education_dialog_vertical_inset"> + <ripple android:color="@color/letterbox_education_dismiss_button_background_ripple"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/letterbox_education_dialog_button_radius"/> + <solid android:color="@android:color/white"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="@android:color/transparent"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/letterbox_education_accent_primary"/> + <corners android:radius="@dimen/letterbox_education_dialog_button_radius"/> + <padding android:left="@dimen/letterbox_education_dialog_horizontal_padding" + android:top="@dimen/letterbox_education_dialog_vertical_padding" + android:right="@dimen/letterbox_education_dialog_horizontal_padding" + android:bottom="@dimen/letterbox_education_dialog_vertical_padding"/> + </shape> + </item> + </ripple> +</inset> diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml deleted file mode 100644 index 60f3cfe6dde6..000000000000 --- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorAccentPrimaryVariant"/> - <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> -</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml index ef97ea19e993..1f125148775d 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml @@ -14,7 +14,31 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/letterbox_restart_button_background_ripple"> - <item android:drawable="@drawable/letterbox_restart_button_background"/> -</ripple>
\ No newline at end of file +<inset xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:insetTop="@dimen/letterbox_restart_dialog_vertical_inset" + android:insetBottom="@dimen/letterbox_restart_dialog_vertical_inset"> + <ripple android:color="@color/letterbox_restart_dismiss_button_background_ripple"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> + <solid android:color="@android:color/white"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="@android:color/transparent"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="?androidprv:attr/colorAccentPrimaryVariant"/> + <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> + <padding android:left="@dimen/letterbox_restart_dialog_horizontal_padding" + android:top="@dimen/letterbox_restart_dialog_vertical_padding" + android:right="@dimen/letterbox_restart_dialog_horizontal_padding" + android:bottom="@dimen/letterbox_restart_dialog_vertical_padding"/> + </shape> + </item> + </ripple> +</inset>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml deleted file mode 100644 index af89d41ee6b5..000000000000 --- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" android:width="1dp"/> - <solid android:color="?androidprv:attr/colorSurface" /> - <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> -</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml index e32aefca78ac..3aa0981e45aa 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml @@ -14,7 +14,33 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/letterbox_restart_dismiss_button_background_ripple"> - <item android:drawable="@drawable/letterbox_restart_dismiss_button_background"/> -</ripple>
\ No newline at end of file +<inset xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:insetTop="@dimen/letterbox_restart_dialog_vertical_inset" + android:insetBottom="@dimen/letterbox_restart_dialog_vertical_inset"> + <ripple android:color="@color/letterbox_restart_dismiss_button_background_ripple"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> + <solid android:color="@android:color/white"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="@android:color/transparent"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" + android:width="1dp"/> + <solid android:color="?androidprv:attr/colorSurface"/> + <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/> + <padding android:left="@dimen/letterbox_restart_dialog_horizontal_padding" + android:top="@dimen/letterbox_restart_dialog_vertical_padding" + android:right="@dimen/letterbox_restart_dialog_horizontal_padding" + android:bottom="@dimen/letterbox_restart_dialog_vertical_padding"/> + </shape> + </item> + </ripple> +</inset>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml index 44b2f45052ba..3d3c00381164 100644 --- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml +++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml @@ -29,11 +29,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:lineSpacingExtra="4sp" + android:letterSpacing="0.02" android:background="@drawable/compat_hint_bubble" android:padding="16dp" android:textAlignment="viewStart" android:textColor="@color/compat_controls_text" - android:textSize="14sp"/> + android:textSize="14sp" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" + /> <ImageView android:layout_width="wrap_content" diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml index c65f24d84e37..095576b581df 100644 --- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml +++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml @@ -29,6 +29,8 @@ android:layout_marginBottom="20dp"/> <TextView + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2" android:id="@+id/letterbox_education_dialog_action_text" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml index 3a44eb9089dd..e8edad15dfc3 100644 --- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml +++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml @@ -69,6 +69,8 @@ android:text="@string/letterbox_education_dialog_title" android:textAlignment="center" android:textColor="@color/compat_controls_text" + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline" android:textSize="24sp"/> <LinearLayout @@ -95,10 +97,16 @@ </LinearLayout> <Button + android:fontFamily="@*android:string/config_bodyFontFamily" + android:fontWeight="500" + android:lineHeight="20dp" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Small" android:id="@+id/letterbox_education_dialog_dismiss_button" + android:textStyle="bold" android:layout_width="match_parent" android:layout_height="56dp" android:layout_marginTop="40dp" + android:textSize="14sp" android:background= "@drawable/letterbox_education_dismiss_button_background_ripple" android:text="@string/letterbox_education_got_it" diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 1f9b6cf6c64f..336c156e831a 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -273,6 +273,18 @@ <!-- The space between two actions in the letterbox education dialog --> <dimen name="letterbox_education_dialog_space_between_actions">24dp</dimen> + <!-- The corner radius of the buttons in the letterbox education dialog --> + <dimen name="letterbox_education_dialog_button_radius">12dp</dimen> + + <!-- The horizontal padding for the buttons in the letterbox education dialog --> + <dimen name="letterbox_education_dialog_horizontal_padding">16dp</dimen> + + <!-- The vertical padding for the buttons in the letterbox education dialog --> + <dimen name="letterbox_education_dialog_vertical_padding">8dp</dimen> + + <!-- The insets for the buttons in the letterbox education dialog --> + <dimen name="letterbox_education_dialog_vertical_inset">6dp</dimen> + <!-- The margin between the dialog container and its parent. --> <dimen name="letterbox_restart_dialog_margin">24dp</dimen> @@ -306,6 +318,15 @@ <!-- The corner radius of the buttons in the restart dialog --> <dimen name="letterbox_restart_dialog_button_radius">18dp</dimen> + <!-- The insets for the buttons in the letterbox restart dialog --> + <dimen name="letterbox_restart_dialog_vertical_inset">6dp</dimen> + + <!-- The horizontal padding for the buttons in the letterbox restart dialog --> + <dimen name="letterbox_restart_dialog_horizontal_padding">16dp</dimen> + + <!-- The vertical padding for the buttons in the letterbox restart dialog --> + <dimen name="letterbox_restart_dialog_vertical_padding">8dp</dimen> + <!-- The width of the brand image on staring surface. --> <dimen name="starting_surface_brand_image_width">200dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index bae009a0526f..0a0c49f2d93f 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -87,6 +87,9 @@ <item name="android:textAppearance"> @*android:style/TextAppearance.DeviceDefault.Headline </item> + <item name="android:fontFamily"> + @*android:string/config_bodyFontFamilyMedium + </item> </style> <style name="RestartDialogBodyText"> @@ -97,24 +100,44 @@ <item name="android:textAppearance"> @*android:style/TextAppearance.DeviceDefault.Body2 </item> + <item name="android:fontFamily"> + @*android:string/config_bodyFontFamily + </item> </style> <style name="RestartDialogCheckboxText"> <item name="android:textSize">16sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:lineSpacingExtra">4sp</item> - <item name="android:textAppearance">@*android:style/TextAppearance.DeviceDefault</item> + <item name="android:textAppearance"> + @*android:style/TextAppearance.DeviceDefault.Headline + </item> + <item name="android:fontFamily"> + @*android:string/config_bodyFontFamilyMedium + </item> </style> <style name="RestartDialogDismissButton"> <item name="android:lineSpacingExtra">2sp</item> <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textAppearance"> + @*android:style/TextAppearance.DeviceDefault.Body2 + </item> + <item name="android:fontFamily"> + @*android:string/config_bodyFontFamily + </item> </style> <style name="RestartDialogConfirmButton"> <item name="android:lineSpacingExtra">2sp</item> <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + <item name="android:textAppearance"> + @*android:style/TextAppearance.DeviceDefault.Body2 + </item> + <item name="android:fontFamily"> + @*android:string/config_bodyFontFamily + </item> </style> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index ee5d205498c3..acf17e670041 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -403,7 +403,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont InputDevice.SOURCE_KEYBOARD); ev.setDisplayId(mContext.getDisplay().getDisplayId()); - if (!InputManager.getInstance() + if (!mContext.getSystemService(InputManager.class) .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) { Log.e(TAG, "Inject input event fail"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java index e7beeeb06534..3a3a378e00d3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java @@ -64,8 +64,8 @@ class BubblesNavBarGestureTracker { stopInternal(); - mInputMonitor = InputManager.getInstance().monitorGestureInput(GESTURE_MONITOR, - mContext.getDisplayId()); + mInputMonitor = mContext.getSystemService(InputManager.class) + .monitorGestureInput(GESTURE_MONITOR, mContext.getDisplayId()); InputChannel inputChannel = mInputMonitor.getInputChannel(); BubblesNavBarMotionEventHandler motionEventHandler = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java new file mode 100644 index 000000000000..20da54efd286 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java @@ -0,0 +1,27 @@ +/* + * 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.draganddrop; + +/** Constants that can be used by both Shell and other users of the library, e.g. Launcher */ +public class DragAndDropConstants { + + /** + * An Intent extra that Launcher can use to specify a region of the screen where Shell should + * ignore drag events. + */ + public static final String EXTRA_DISALLOW_HIT_REGION = "DISALLOW_HIT_REGION"; +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index d93a9012c8f1..df94b414c092 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -34,6 +34,7 @@ import static android.content.Intent.EXTRA_USER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; +import static com.android.wm.shell.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT; @@ -53,6 +54,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.LauncherApps; import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; @@ -86,6 +88,7 @@ public class DragAndDropPolicy { private final Starter mStarter; private final SplitScreenController mSplitScreen; private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>(); + private final RectF mDisallowHitRegion = new RectF(); private InstanceId mLoggerSessionId; private DragSession mSession; @@ -111,6 +114,12 @@ public class DragAndDropPolicy { mSession = new DragSession(mActivityTaskManager, displayLayout, data); // TODO(b/169894807): Also update the session data with task stack changes mSession.update(); + RectF disallowHitRegion = (RectF) mSession.dragData.getExtra(EXTRA_DISALLOW_HIT_REGION); + if (disallowHitRegion == null) { + mDisallowHitRegion.setEmpty(); + } else { + mDisallowHitRegion.set(disallowHitRegion); + } } /** @@ -218,6 +227,9 @@ public class DragAndDropPolicy { */ @Nullable Target getTargetAtLocation(int x, int y) { + if (mDisallowHitRegion.contains(x, y)) { + return null; + } for (int i = mTargets.size() - 1; i >= 0; i--) { DragAndDropPolicy.Target t = mTargets.get(i); if (t.hitRegion.contains(x, y)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index bb6e3917ff3c..44fd8eec4d06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -118,7 +118,7 @@ public class DragLayout extends LinearLayout { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - mInsets = insets.getInsets(Type.systemBars() | Type.displayCutout()); + mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout()); recomputeDropTargets(); final int orientation = getResources().getConfiguration().orientation; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 41ff0b35a035..fee9140d0c2c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -228,7 +228,7 @@ public class PipResizeGestureHandler { if (mIsEnabled) { // Register input event receiver - mInputMonitor = InputManager.getInstance().monitorGestureInput( + mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput( "pip-resize", mDisplayId); try { mMainExecutor.executeBlocking(() -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 19d8cfac559a..7833cfe7df9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1705,9 +1705,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - setDividerVisibility(mainStageVisible, t); - }); + setDividerVisibility(mainStageVisible, null); } private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) { @@ -1789,6 +1787,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationEnd(Animator animation) { + if (dividerLeash != null && dividerLeash.isValid()) { + transaction.setAlpha(dividerLeash, 1); + transaction.apply(); + } mTransactionPool.release(transaction); mDividerFadeInAnimator = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index c2da70595a3d..8e916e63cac6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -186,7 +186,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { private boolean isHomeOpening(@NonNull TransitionInfo.Change change) { return change.getTaskInfo() != null - && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME; + && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; } private boolean isWallpaper(@NonNull TransitionInfo.Change change) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index de5f2f467e99..75bc98555665 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -505,7 +505,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } private void createInputChannel(int displayId) { - final InputManager inputManager = InputManager.getInstance(); + final InputManager inputManager = mContext.getSystemService(InputManager.class); final InputMonitor inputMonitor = mInputMonitorFactory.create(inputManager, mContext); final EventReceiver eventReceiver = new EventReceiver(inputMonitor, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java index aea340464304..d0fcd8651481 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java @@ -65,7 +65,7 @@ class TaskOperations { InputDevice.SOURCE_KEYBOARD); ev.setDisplayId(mContext.getDisplay().getDisplayId()); - if (!InputManager.getInstance() + if (!mContext.getSystemService(InputManager.class) .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) { Log.e(TAG, "Inject input event fail"); } diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index cbebae9ed438..ecf6cfc05444 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -464,7 +464,7 @@ bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* str SkBitmap baseBitmap = getSkBitmap(); SkBitmap gainmapBitmap = gainmap()->bitmap->getSkBitmap(); SkJpegEncoder::Options options{.fQuality = quality}; - return SkJpegGainmapEncoder::EncodeJpegR(stream, baseBitmap.pixmap(), options, + return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options, gainmapBitmap.pixmap(), options, gainmap()->info); } #endif diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 40a4858d30ba..d2b21ae19162 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -849,7 +849,7 @@ public class RingtoneManager { if (ringtoneUri != null) { final Uri cacheUri = getCacheForType(type, context.getUserId()); try (InputStream in = openRingtone(context, ringtoneUri); - OutputStream out = resolver.openOutputStream(cacheUri)) { + OutputStream out = resolver.openOutputStream(cacheUri, "wt")) { FileUtils.copy(in, out); } catch (IOException e) { Log.w(TAG, "Failed to cache ringtone: " + e); diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java index 95b49a8ef335..73f87302dd05 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java @@ -21,7 +21,6 @@ import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupManagerMonitor; -import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.app.backup.BackupTransport; import android.app.backup.RestoreDescription; @@ -48,6 +47,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; /** @@ -915,6 +915,7 @@ public class LocalTransport extends BackupTransport { Log.i(TAG, "\tdataType: " + result.getDataType()); Log.i(TAG, "\tsuccessCount: " + result.getSuccessCount()); Log.i(TAG, "\tfailCount: " + result.getFailCount()); + Log.i(TAG, "\tmetadataHash: " + Arrays.toString(result.getMetadataHash())); if (!result.getErrors().isEmpty()) { Log.i(TAG, "\terrors {"); diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml index 5a9e780df8e9..cc60382b48f9 100644 --- a/packages/SettingsLib/res/values/styles.xml +++ b/packages/SettingsLib/res/values/styles.xml @@ -14,7 +14,7 @@ ~ limitations under the License --> -<resources> +<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <style name="TextAppearanceSmall"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> </style> @@ -73,7 +73,7 @@ </style> <style name="TextAppearanceBroadcastDialogButton" parent="@android:TextAppearance.DeviceDefault.Headline"> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textColor">?androidprv:attr/textColorOnAccent</item> <item name="android:textSize">@dimen/broadcast_dialog_btn_text_size</item> </style> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt index a1d9d90523eb..e42b589f05cf 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt @@ -24,7 +24,7 @@ import com.android.systemui.animation.LaunchableView import com.android.systemui.animation.LaunchableViewDelegate /** An [ImageView] that also implements [LaunchableView]. */ -class LaunchableImageView : ImageView, LaunchableView { +open class LaunchableImageView : ImageView, LaunchableView { private val delegate = LaunchableViewDelegate( this, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt index 286996dcaeaa..147669528c5e 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt @@ -24,7 +24,7 @@ import com.android.systemui.animation.LaunchableView import com.android.systemui.animation.LaunchableViewDelegate /** A [TextView] that also implements [LaunchableView]. */ -class LaunchableTextView : TextView, LaunchableView { +open class LaunchableTextView : TextView, LaunchableView { private val delegate = LaunchableViewDelegate( this, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index eec788b7add8..f164e7d33642 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -492,12 +492,14 @@ public class KeyguardSecurityContainer extends ConstraintLayout { case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); int pointerIndex = event.findPointerIndex(mActivePointerId); - float y = event.getY(pointerIndex); - if (mLastTouchY != -1) { - float dy = y - mLastTouchY; - setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER); + if (pointerIndex != -1) { + float y = event.getY(pointerIndex); + if (mLastTouchY != -1) { + float dy = y - mLastTouchY; + setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER); + } + mLastTouchY = y; } - mLastTouchY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 23f887ef0016..f1abdc68f97e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -17,7 +17,6 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; -import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS; @@ -36,7 +35,6 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricSourceType; import android.media.AudioManager; import android.metrics.LogMaker; import android.os.SystemClock; @@ -321,7 +319,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard KeyguardSecurityContainerController.this.onDensityOrFontScaleChanged(); } }; - private boolean mBouncerVisible = false; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -356,19 +353,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public void onDevicePolicyManagerStateChanged() { showPrimarySecurityScreen(false); } - - @Override - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - if (biometricSourceType == FINGERPRINT) { - updateSideFpsVisibility(); - } - } - - @Override - public void onStrongAuthStateChanged(int userId) { - updateSideFpsVisibility(); - } }; @Inject @@ -460,35 +444,24 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard getCurrentSecurityController().onPause(); } mView.onPause(); - // It might happen that onStartingToHide is not called when the device is locked while on - // bouncer. - setBouncerVisible(false); mView.clearFocus(); } - private void updateSideFpsVisibility() { + /** + * Shows and hides the side finger print sensor animation. + * + * @param isVisible sets whether we show or hide the side fps animation + */ + public void updateSideFpsVisibility(boolean isVisible) { if (!mSideFpsController.isPresent()) { return; } - final boolean sfpsEnabled = getResources().getBoolean( - R.bool.config_show_sidefps_hint_on_bouncer); - final boolean fpsDetectionRunning = mUpdateMonitor.isFingerprintDetectionRunning(); - final boolean isUnlockingWithFpAllowed = - mUpdateMonitor.isUnlockingWithFingerprintAllowed(); - boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning - && isUnlockingWithFpAllowed; - - if (DEBUG) { - Log.d(TAG, "sideFpsToShow=" + toShow + ", " - + "mBouncerVisible=" + mBouncerVisible + ", " - + "configEnabled=" + sfpsEnabled + ", " - + "fpsDetectionRunning=" + fpsDetectionRunning + ", " - + "isUnlockingWithFpAllowed=" + isUnlockingWithFpAllowed); - } - if (toShow) { - mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); + if (isVisible) { + mSideFpsController.get().show( + SideFpsUiRequestSource.PRIMARY_BOUNCER, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD + ); } else { mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); } @@ -637,7 +610,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, state); getCurrentSecurityController().onResume(reason); - updateSideFpsVisibility(); } mView.onResume( mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()), @@ -691,22 +663,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (mCurrentSecurityMode != SecurityMode.None) { getCurrentSecurityController().onStartingToHide(); } - setBouncerVisible(false); } /** Called when the bouncer changes visibility. */ - public void onBouncerVisibilityChanged(@View.Visibility int visibility) { - setBouncerVisible(visibility == View.VISIBLE); - if (visibility == View.INVISIBLE) { + public void onBouncerVisibilityChanged(boolean isVisible) { + if (!isVisible) { mView.resetScale(); } } - private void setBouncerVisible(boolean visible) { - mBouncerVisible = visible; - updateSideFpsVisibility(); - } - /** * Shows the next security screen if there is one. * @param authenticated true if the user entered the correct authentication diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index b8e1499a5ae7..eb4b05b42b91 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -96,8 +96,10 @@ import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; @@ -2942,7 +2944,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Trigger the fingerprint success path so the bouncer can be shown handleFingerprintAuthenticated(user, isStrongBiometric); }, - userId); + new FingerprintAuthenticateOptions.Builder() + .setUserId(userId) + .build()); } else { mLogger.v("startListeningForFingerprint - authenticate"); mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal, @@ -2988,7 +2992,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (supportsFaceDetection && !udfpsFingerprintAuthRunning) { // Run face detection. (If a face is detected, show the bouncer.) mLogger.v("startListeningForFace - detect"); - mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId); + mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, + new FaceAuthenticateOptions.Builder() + .setUserId(userId) + .build()); } else { // Don't run face detection. Instead, inform the user // face auth is unavailable and how to proceed. @@ -3007,7 +3014,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean isBypassEnabled = mKeyguardBypassController != null && mKeyguardBypassController.isBypassEnabled(); mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal, - mFaceAuthenticationCallback, null /* handler */, userId, isBypassEnabled); + mFaceAuthenticationCallback, null /* handler */, userId); } setFaceRunningState(BIOMETRIC_STATE_RUNNING); } diff --git a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt index 0f00a040b094..603471b1de41 100644 --- a/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt +++ b/packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt @@ -73,6 +73,10 @@ class ScreenOnCoordinator @Inject constructor( @BinderThread fun onScreenTurnedOn() { foldAodAnimationController?.onScreenTurnedOn() + } + + @BinderThread + fun onScreenTurnedOff() { pendingTasks.reset() } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 00f5ac2ffa23..12b5705190d9 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -58,13 +58,13 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.util.Assert; +import dagger.Lazy; + import java.util.Locale; import java.util.Optional; import javax.inject.Inject; -import dagger.Lazy; - /** * Class to register system actions with accessibility framework. */ @@ -473,7 +473,7 @@ public class SystemActions implements CoreStartable { KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD, null); - InputManager.getInstance() + mContext.getSystemService(InputManager.class) .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); event.recycle(); } diff --git a/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt new file mode 100644 index 000000000000..1390b4db3576 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import android.content.ComponentName + +/** Returns true if the admin of [userId] disallows keyguard shortcuts. */ +fun DevicePolicyManager.areKeyguardShortcutsDisabled( + admin: ComponentName? = null, + userId: Int +): Boolean { + val flags = getKeyguardDisabledFeatures(admin, userId) + return flags and KEYGUARD_DISABLE_SHORTCUTS_ALL == KEYGUARD_DISABLE_SHORTCUTS_ALL || + flags and KEYGUARD_DISABLE_FEATURES_ALL == KEYGUARD_DISABLE_FEATURES_ALL +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index fa4caaf8960d..2e37b348c5e3 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -62,7 +62,7 @@ object Flags { val FSI_REQUIRES_KEYGUARD = releasedFlag(110, "fsi_requires_keyguard") // TODO(b/259130119): Tracking Bug - val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true) + val FSI_ON_DND_UPDATE = releasedFlag(259130119, "fsi_on_dnd_update") // TODO(b/265804648): Tracking Bug @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi") @@ -266,6 +266,12 @@ object Flags { @JvmField val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = false) + /** Enables new QS Edit Mode visual refresh */ + // TODO(b/269787742): Tracking Bug + @JvmField + val ENABLE_NEW_QS_EDIT_MODE = + unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false) + // 600- status bar // TODO(b/256614753): Tracking Bug @@ -630,7 +636,8 @@ object Flags { // 2600 - keyboard // TODO(b/259352579): Tracking Bug - @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout") + @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = + unreleasedFlag(2600, "shortcut_list_search_layout", teamfood = true) // TODO(b/259428678): Tracking Bug @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt index e9b8908214fc..496c64e1120e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt @@ -17,6 +17,14 @@ package com.android.systemui.keyboard +import com.android.systemui.keyboard.data.repository.KeyboardRepository +import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl +import dagger.Binds import dagger.Module -@Module abstract class KeyboardModule +@Module +abstract class KeyboardModule { + + @Binds + abstract fun bindKeyboardRepository(repository: KeyboardRepositoryImpl): KeyboardRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt new file mode 100644 index 000000000000..ea15a9f18584 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.data.model + +/** + * Model for current state of keyboard backlight brightness. [level] indicates current level of + * backlight brightness and [maxLevel] its max possible value. + */ +data class BacklightModel(val level: Int, val maxLevel: Int) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt new file mode 100644 index 000000000000..70faf406d621 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.data.repository + +import android.hardware.input.InputManager +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyboard.data.model.BacklightModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn + +interface KeyboardRepository { + val keyboardConnected: Flow<Boolean> + val backlight: Flow<BacklightModel> +} + +@SysUISingleton +class KeyboardRepositoryImpl +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val inputManager: InputManager, +) : KeyboardRepository { + + private val connectedDeviceIds: Flow<Set<Int>> = + conflatedCallbackFlow { + fun send(element: Set<Int>) = trySendWithFailureLogging(element, TAG) + + var connectedKeyboards = inputManager.inputDeviceIds.toSet() + val listener = + object : InputManager.InputDeviceListener { + override fun onInputDeviceAdded(deviceId: Int) { + connectedKeyboards = connectedKeyboards + deviceId + send(connectedKeyboards) + } + + override fun onInputDeviceChanged(deviceId: Int) = Unit + + override fun onInputDeviceRemoved(deviceId: Int) { + connectedKeyboards = connectedKeyboards - deviceId + send(connectedKeyboards) + } + } + send(connectedKeyboards) + inputManager.registerInputDeviceListener(listener, /* handler= */ null) + awaitClose { inputManager.unregisterInputDeviceListener(listener) } + } + .shareIn( + scope = applicationScope, + started = SharingStarted.Lazily, + replay = 1, + ) + + override val keyboardConnected: Flow<Boolean> = + connectedDeviceIds + .map { it.any { deviceId -> isPhysicalFullKeyboard(deviceId) } } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + + override val backlight: Flow<BacklightModel> = + conflatedCallbackFlow { + // TODO(b/268645734) register BacklightListener + } + + private fun isPhysicalFullKeyboard(deviceId: Int): Boolean { + val device = inputManager.getInputDevice(deviceId) + return !device.isVirtual && device.isFullKeyboard + } + + companion object { + const val TAG = "KeyboardRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 7a891b0058af..eef7ccc65d95 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -602,6 +602,7 @@ public class KeyguardService extends Service { checkPermission(); mKeyguardViewMediator.onScreenTurnedOff(); mKeyguardLifecyclesDispatcher.dispatch(KeyguardLifecyclesDispatcher.SCREEN_TURNED_OFF); + mScreenOnCoordinator.onScreenTurnedOff(); } @Override // Binder interface diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 54fc5b588db7..02bee3efbe2f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -59,6 +59,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.SoundPool; +import android.os.Binder; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; @@ -67,6 +68,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; @@ -102,6 +104,7 @@ import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardStateCallback; import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardConstants; @@ -270,6 +273,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private AlarmManager mAlarmManager; private AudioManager mAudioManager; private StatusBarManager mStatusBarManager; + private final IStatusBarService mStatusBarService; + private final IBinder mStatusBarDisableToken = new Binder(); private final UserTracker mUserTracker; private final SysuiStatusBarStateController mStatusBarStateController; private final Executor mUiBgExecutor; @@ -1213,6 +1218,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mPM = powerManager; mTrustManager = trustManager; mUserSwitcherController = userSwitcherController; + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mKeyguardDisplayManager = keyguardDisplayManager; mShadeController = shadeControllerLazy; dumpManager.registerDumpable(getClass().getName(), this); @@ -2918,7 +2925,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // TODO (b/155663717) After restart, status bar will not properly hide home button // unless disable is called to show un-hide it once first if (forceClearFlags) { - mStatusBarManager.disable(flags); + try { + mStatusBarService.disableForUser(flags, mStatusBarDisableToken, + mContext.getPackageName(), mUserTracker.getUserId()); + } catch (RemoteException e) { + Log.d(TAG, "Failed to force clear flags", e); + } } if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) { @@ -2934,7 +2946,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, + " --> flags=0x" + Integer.toHexString(flags)); } - mStatusBarManager.disable(flags); + try { + mStatusBarService.disableForUser(flags, mStatusBarDisableToken, + mContext.getPackageName(), mUserTracker.getUserId()); + } catch (RemoteException e) { + Log.d(TAG, "Failed to set disable flags: " + flags, e); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 4331fe66a0dc..0e85347c24b0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -60,7 +60,6 @@ interface KeyguardBouncerRepository { */ val panelExpansionAmount: StateFlow<Float> val keyguardPosition: StateFlow<Float> - val onScreenTurnedOff: StateFlow<Boolean> val isBackButtonEnabled: StateFlow<Boolean?> /** Determines if user is already unlocked */ val keyguardAuthenticated: StateFlow<Boolean?> @@ -70,6 +69,8 @@ interface KeyguardBouncerRepository { val bouncerErrorMessage: CharSequence? val alternateBouncerVisible: StateFlow<Boolean> val alternateBouncerUIAvailable: StateFlow<Boolean> + val sideFpsShowing: StateFlow<Boolean> + var lastAlternateBouncerVisibleTime: Long fun setPrimaryScrimmed(isScrimmed: Boolean) @@ -98,11 +99,11 @@ interface KeyguardBouncerRepository { fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) - fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) - fun setAlternateVisible(isVisible: Boolean) fun setAlternateBouncerUIAvailable(isAvailable: Boolean) + + fun setSideFpsShowing(isShowing: Boolean) } @SysUISingleton @@ -142,8 +143,6 @@ constructor( override val panelExpansionAmount = _panelExpansionAmount.asStateFlow() private val _keyguardPosition = MutableStateFlow(0f) override val keyguardPosition = _keyguardPosition.asStateFlow() - private val _onScreenTurnedOff = MutableStateFlow(false) - override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow() private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null) override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow() private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null) @@ -165,6 +164,8 @@ constructor( private val _alternateBouncerUIAvailable = MutableStateFlow(false) override val alternateBouncerUIAvailable: StateFlow<Boolean> = _alternateBouncerUIAvailable.asStateFlow() + private val _sideFpsShowing = MutableStateFlow(false) + override val sideFpsShowing: StateFlow<Boolean> = _sideFpsShowing.asStateFlow() init { setUpLogging() @@ -235,8 +236,8 @@ constructor( _isBackButtonEnabled.value = isBackButtonEnabled } - override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) { - _onScreenTurnedOff.value = onScreenTurnedOff + override fun setSideFpsShowing(isShowing: Boolean) { + _sideFpsShowing.value = isShowing } /** Sets up logs for state flows. */ @@ -276,9 +277,6 @@ constructor( .map { it.toInt() } .logDiffsForTable(buffer, "", "KeyguardPosition", -1) .launchIn(applicationScope) - onScreenTurnedOff - .logDiffsForTable(buffer, "", "OnScreenTurnedOff", false) - .launchIn(applicationScope) isBackButtonEnabled .filterNotNull() .logDiffsForTable(buffer, "", "IsBackButtonEnabled", false) @@ -293,6 +291,9 @@ constructor( alternateBouncerUIAvailable .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false) .launchIn(applicationScope) + sideFpsShowing + .logDiffsForTable(buffer, "", "isSideFpsShowing", false) + .launchIn(applicationScope) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt index 2069891a23e0..a3268405a830 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.app.StatusBarManager import android.content.Context +import android.hardware.face.FaceAuthenticateOptions import android.hardware.face.FaceManager import android.os.CancellationSignal import com.android.internal.logging.InstanceId @@ -235,8 +236,7 @@ constructor( cancellationSignal, faceAuthCallback, null, - currentUserId, - lockscreenBypassEnabled + FaceAuthenticateOptions.Builder().setUserId(currentUserId).build() ) } } @@ -255,7 +255,11 @@ constructor( withContext(mainDispatcher) { // We always want to invoke face detect in the main thread. faceAuthLogger.faceDetectionStarted() - faceManager?.detectFace(cancellationSignal, detectionCallback, currentUserId) + faceManager?.detectFace( + cancellationSignal, + detectionCallback, + FaceAuthenticateOptions.Builder().setUserId(currentUserId).build() + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index dfbe1c216847..9b5f7f606c60 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -26,6 +26,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig @@ -410,16 +411,10 @@ constructor( ) } - private suspend fun isFeatureDisabledByDevicePolicy(): Boolean { - val flags = - withContext(backgroundDispatcher) { - devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) - } - val flagsToCheck = - DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or - DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL - return flagsToCheck and flags != 0 - } + private suspend fun isFeatureDisabledByDevicePolicy(): Boolean = + withContext(backgroundDispatcher) { + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + } companion object { private const val TAG = "KeyguardQuickAffordanceInteractor" diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index e819da9a50be..edd28972c05e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -16,14 +16,19 @@ package com.android.systemui.keyguard.domain.interactor +import android.content.Context import android.content.res.ColorStateList import android.hardware.biometrics.BiometricSourceType import android.os.Handler import android.os.Trace import android.view.View +import android.util.Log +import com.android.keyguard.KeyguardConstants import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.DejankUtils +import com.android.systemui.R import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main @@ -60,8 +65,9 @@ constructor( private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor, private val falsingCollector: FalsingCollector, private val dismissCallbackRegistry: DismissCallbackRegistry, + private val context: Context, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, keyguardBypassController: KeyguardBypassController, - keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { /** Whether we want to wait for face auth. */ private val primaryBouncerFaceDelay = @@ -88,7 +94,6 @@ constructor( } val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull() - val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {} val show: Flow<KeyguardBouncerModel> = repository.primaryBouncerShow.filterNotNull() val hide: Flow<Unit> = repository.primaryBouncerHide.filter { it }.map {} val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {} @@ -113,6 +118,24 @@ constructor( } /** Allow for interaction when just about fully visible */ val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 } + val sideFpsShowing: Flow<Boolean> = repository.sideFpsShowing + + init { + keyguardUpdateMonitor.registerCallback( + object : KeyguardUpdateMonitorCallback() { + override fun onBiometricRunningStateChanged( + running: Boolean, + biometricSourceType: BiometricSourceType? + ) { + updateSideFpsVisibility() + } + + override fun onStrongAuthStateChanged(userId: Int) { + updateSideFpsVisibility() + } + } + ) + } // TODO(b/243685699): Move isScrimmed logic to data layer. // TODO(b/243695312): Encapsulate all of the show logic for the bouncer. @@ -120,7 +143,6 @@ constructor( @JvmOverloads fun show(isScrimmed: Boolean) { // Reset some states as we show the bouncer. - repository.setOnScreenTurnedOff(false) repository.setKeyguardAuthenticated(null) repository.setPrimaryHide(false) repository.setPrimaryStartingToHide(false) @@ -254,11 +276,6 @@ constructor( repository.setKeyguardAuthenticated(strongAuth) } - /** Tell the bouncer the screen has turned off. */ - fun onScreenTurnedOff() { - repository.setOnScreenTurnedOff(true) - } - /** Update the position of the bouncer when showing. */ fun setKeyguardPosition(position: Float) { repository.setKeyguardPosition(position) @@ -293,6 +310,35 @@ constructor( repository.setPrimaryStartDisappearAnimation(finishRunnable) } + /** Determine whether to show the side fps animation. */ + fun updateSideFpsVisibility() { + val sfpsEnabled: Boolean = + context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) + val fpsDetectionRunning: Boolean = keyguardUpdateMonitor.isFingerprintDetectionRunning + val isUnlockingWithFpAllowed: Boolean = + keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed + val bouncerVisible = repository.primaryBouncerVisible.value + val toShow = + (repository.primaryBouncerVisible.value && + sfpsEnabled && + fpsDetectionRunning && + isUnlockingWithFpAllowed && + !isAnimatingAway()) + + if (KeyguardConstants.DEBUG) { + Log.d( + TAG, + ("sideFpsToShow=$toShow\n" + + "bouncerVisible=$bouncerVisible\n" + + "configEnabled=$sfpsEnabled\n" + + "fpsDetectionRunning=$fpsDetectionRunning\n" + + "isUnlockingWithFpAllowed=$isUnlockingWithFpAllowed\n" + + "isAnimatingAway=${isAnimatingAway()}") + ) + } + repository.setSideFpsShowing(toShow) + } + /** Returns whether bouncer is fully showing. */ fun isFullyShowing(): Boolean { return (repository.primaryBouncerShowingSoon.value || @@ -336,4 +382,8 @@ constructor( DejankUtils.removeCallbacks(showRunnable) mainHandler.removeCallbacks(showRunnable) } + + companion object { + private const val TAG = "PrimaryBouncerInteractor" + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 56f911f8b1da..7db567b2a0e9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.ActivityStarter import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -121,7 +122,6 @@ object KeyguardBouncerViewBinder { launch { viewModel.hide.collect { securityContainerController.cancelDismissAction() - securityContainerController.onPause() securityContainerController.reset() } } @@ -155,13 +155,18 @@ object KeyguardBouncerViewBinder { launch { viewModel.isBouncerVisible.collect { isVisible -> - val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE - view.visibility = visibility - securityContainerController.onBouncerVisibilityChanged(visibility) + view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + securityContainerController.onBouncerVisibilityChanged(isVisible) } } launch { + viewModel.isBouncerVisible + .filter { !it } + .collect { securityContainerController.onPause() } + } + + launch { viewModel.isInteractable.collect { isInteractable -> securityContainerController.setInteractable(isInteractable) } @@ -204,10 +209,14 @@ object KeyguardBouncerViewBinder { } launch { - viewModel.screenTurnedOff.collect { - if (view.visibility == View.VISIBLE) { - securityContainerController.onPause() - } + viewModel.shouldUpdateSideFps.collect { + viewModel.updateSideFpsVisibility() + } + } + + launch { + viewModel.sideFpsShowing.collect { + securityContainerController.updateSideFpsVisibility(it) } } awaitCancellation() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt index b8b3a8e5db20..97e94d8f3232 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -24,7 +24,9 @@ import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge /** Models UI state for the lock screen bouncer; handles user input. */ class KeyguardBouncerViewModel @@ -66,8 +68,16 @@ constructor( /** Observe whether keyguard is authenticated already. */ val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticated - /** Observe whether screen is turned off. */ - val screenTurnedOff: Flow<Unit> = interactor.screenTurnedOff + /** Observe whether the side fps is showing. */ + val sideFpsShowing: Flow<Boolean> = interactor.sideFpsShowing + + /** Observe whether we should update fps is showing. */ + val shouldUpdateSideFps: Flow<Unit> = + merge( + interactor.startingToHide, + interactor.isVisible.map {}, + interactor.startingDisappearAnimation.filterNotNull().map {} + ) /** Observe whether we want to update resources. */ fun notifyUpdateResources() { @@ -84,6 +94,10 @@ constructor( interactor.onMessageShown() } + fun updateSideFpsVisibility() { + interactor.updateSideFpsVisibility() + } + /** Observe whether back button is enabled. */ fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> { return interactor.isBackButtonEnabled.map { enabled -> diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index c3d736917b5d..f1a5c3e8ef43 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -75,6 +75,7 @@ import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.BarTransitions; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LightBarTransitionsController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.pip.Pip; @@ -166,16 +167,20 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, private BackAnimation mBackAnimation; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Inject public TaskbarDelegate(Context context, EdgeBackGestureHandler.Factory edgeBackGestureHandlerFactory, - LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) { + LightBarTransitionsController.Factory lightBarTransitionsControllerFactory, + StatusBarKeyguardViewManager statusBarKeyguardViewManager) { mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory; mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context); mContext = context; mDisplayManager = mContext.getSystemService(DisplayManager.class); mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mStatusBarKeyguardViewManager.setTaskbarDelegate(this); } public void setDependencies(CommandQueue commandQueue, diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 389034af8a33..f3d60145a057 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -580,7 +580,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } // Register input event receiver - mInputMonitor = InputManager.getInstance().monitorGestureInput( + mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput( "edge-swipe", mDisplayId); mInputEventReceiver = new InputChannelCompat.InputEventReceiver( mInputMonitor.getInputChannel(), Looper.getMainLooper(), @@ -1039,7 +1039,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack InputDevice.SOURCE_KEYBOARD); ev.setDisplayId(mContext.getDisplay().getDisplayId()); - return InputManager.getInstance() + return mContext.getSystemService(InputManager.class) .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index be615d63a3d7..5702681d9240 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -17,17 +17,21 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.os.Build import android.os.UserManager import android.util.Log import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker import com.android.systemui.util.kotlin.getOrNull import com.android.wm.shell.bubbles.Bubbles import java.util.Optional @@ -49,8 +53,10 @@ constructor( private val optionalBubbles: Optional<Bubbles>, private val optionalKeyguardManager: Optional<KeyguardManager>, private val optionalUserManager: Optional<UserManager>, + private val devicePolicyManager: DevicePolicyManager, @NoteTaskEnabledKey private val isEnabled: Boolean, private val uiEventLogger: UiEventLogger, + private val userTracker: UserTracker, ) { /** @@ -80,6 +86,18 @@ constructor( // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return + val isKeyguardLocked = keyguardManager.isKeyguardLocked + // KeyguardQuickAffordanceInteractor blocks the quick affordance from showing in the + // keyguard if it is not allowed by the admin policy. Here we block any other way to show + // note task when the screen is locked. + if ( + isKeyguardLocked && + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + ) { + logDebug { "Enterprise policy disallows launching note app when the screen is locked." } + return + } + val noteTaskInfo = resolver.resolveInfo() ?: return uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) } @@ -87,7 +105,7 @@ constructor( // TODO(b/266686199): We should handle when app not available. For now, we log. val intent = noteTaskInfo.toCreateNoteIntent() try { - if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) { + if (isInMultiWindowMode || isKeyguardLocked) { context.startActivity(intent) } else { bubbles.showOrHideAppBubble(intent) @@ -144,7 +162,7 @@ constructor( } companion object { - private val TAG = NoteTaskController::class.simpleName.orEmpty() + val TAG = NoteTaskController::class.simpleName.orEmpty() private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent { return Intent(ACTION_CREATE_NOTE) @@ -165,3 +183,9 @@ constructor( const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" } } + +private inline fun logDebug(message: () -> String) { + if (Build.IS_DEBUGGABLE) { + Log.d(NoteTaskController.TAG, message()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index f4640ad2e3cd..a5da06e3f1e5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1197,6 +1197,7 @@ public final class NotificationPanelViewController implements Dumpable { } private void onSplitShadeEnabledChanged() { + mShadeLog.logSplitShadeChanged(mSplitShadeEnabled); // when we switch between split shade and regular shade we want to enforce setting qs to // the default state: expanded for split shade and collapsed otherwise if (!isOnKeyguard() && mPanelExpanded) { @@ -1777,7 +1778,7 @@ public final class NotificationPanelViewController implements Dumpable { if (animate && !isFullyCollapsed()) { animateCloseQs(true); } else { - mQsController.closeQs(); + closeQsIfPossible(); } mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate, !animate /* cancelAnimators */); @@ -1914,7 +1915,6 @@ public final class NotificationPanelViewController implements Dumpable { // we want to perform an overshoot animation when flinging open final boolean addOverscroll = expand - && !mSplitShadeEnabled // Split shade has its own overscroll logic && mStatusBarStateController.getState() != KEYGUARD && mOverExpansion == 0.0f && vel >= 0; @@ -2590,9 +2590,14 @@ public final class NotificationPanelViewController implements Dumpable { return; } mOverExpansion = overExpansion; - // Translating the quick settings by half the overexpansion to center it in the background - // frame - mQsController.updateQsFrameTranslation(); + if (mSplitShadeEnabled) { + mQsController.setOverScrollAmount((int) overExpansion); + mScrimController.setNotificationsOverScrollAmount((int) overExpansion); + } else { + // Translating the quick settings by half the overexpansion to center it in the + // background frame + mQsController.updateQsFrameTranslation(); + } mNotificationStackScrollLayoutController.setOverExpansion(overExpansion); } @@ -3594,7 +3599,7 @@ public final class NotificationPanelViewController implements Dumpable { private void fling(float vel, boolean expand, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { - float target = expand ? getMaxPanelHeight() : 0; + float target = expand ? getMaxPanelTransitionDistance() : 0; if (!expand) { setClosing(true); } @@ -3679,7 +3684,7 @@ public final class NotificationPanelViewController implements Dumpable { float maxPanelHeight = getMaxPanelTransitionDistance(); if (mHeightAnimator == null) { // Split shade has its own overscroll logic - if (mTracking && !mSplitShadeEnabled) { + if (mTracking) { float overExpansionPixels = Math.max(0, h - maxPanelHeight); setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */); } @@ -4025,9 +4030,17 @@ public final class NotificationPanelViewController implements Dumpable { return mExpandingFromHeadsUp; } - /** TODO: remove need for this delegate (b/254870148) */ - public void closeQs() { - mQsController.closeQs(); + /** + * We don't always want to close QS when requested as shade might be in a different state + * already e.g. when going from collapse to expand very quickly. In that case StatusBar + * window might send signal to collapse QS but we might be already expanding and in split + * shade QS are always expanded + */ + private void closeQsIfPossible() { + boolean openOrOpening = isShadeFullyOpen() || isExpanding(); + if (!(mSplitShadeEnabled && openOrOpening)) { + mQsController.closeQs(); + } } /** TODO: remove need for this delegate (b/254870148) */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index d041212d24c7..a8378df81968 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -663,8 +663,15 @@ public class QuickSettingsController { mDozing = dozing; } - /** set QS state to closed */ + /** + * This method closes QS but in split shade it should be used only in special cases: to make + * sure QS closes when shade is closed as well. Otherwise it will result in QS disappearing + * from split shade + */ public void closeQs() { + if (mSplitShadeEnabled) { + mShadeLog.d("Closing QS while in split shade"); + } cancelExpansionAnimation(); setExpansionHeight(getMinExpansionHeight()); // qsExpandImmediate is a safety latch in case we're calling closeQS while we're in the @@ -803,6 +810,10 @@ public class QuickSettingsController { } } + void setOverScrollAmount(int overExpansion) { + mQs.setOverScrollAmount(overExpansion); + } + private void setOverScrolling(boolean overscrolling) { mStackScrollerOverscrolling = overscrolling; if (mQs != null) { @@ -1710,12 +1721,16 @@ public class QuickSettingsController { */ private void flingQs(float vel, int type, final Runnable onFinishRunnable, boolean isClick) { + mShadeLog.flingQs(type, isClick); float target; switch (type) { case FLING_EXPAND: target = getMaxExpansionHeight(); break; case FLING_COLLAPSE: + if (mSplitShadeEnabled) { // TODO:(b/269742565) remove below log + Log.wtfStack(TAG, "FLING_COLLAPSE called in split shade"); + } target = getMinExpansionHeight(); break; case FLING_HIDE: diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index 026673adb86b..c1369935db54 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -270,8 +270,6 @@ public final class ShadeControllerImpl implements ShadeController { // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) mNotificationPanelViewController.collapsePanel(false, false, 1.0f); - mNotificationPanelViewController.closeQs(); - mExpandedVisible = false; notifyVisibilityChanged(false); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt index aa8c5b65e0fe..d34e127b194b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt @@ -20,6 +20,9 @@ import android.view.MotionEvent import com.android.systemui.log.dagger.ShadeLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE +import com.android.systemui.shade.NotificationPanelViewController.FLING_EXPAND +import com.android.systemui.shade.NotificationPanelViewController.FLING_HIDE import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -241,18 +244,40 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) { ) } - fun logLastFlingWasExpanding( - expand: Boolean - ) { + fun logLastFlingWasExpanding(expand: Boolean) { buffer.log( - TAG, - LogLevel.VERBOSE, - { - bool1 = expand - }, - { - "NPVC mLastFlingWasExpanding set to: $bool1" - } + TAG, + LogLevel.VERBOSE, + { bool1 = expand }, + { "NPVC mLastFlingWasExpanding set to: $bool1" } + ) + } + + fun flingQs(flingType: Int, isClick: Boolean) { + buffer.log( + TAG, + LogLevel.VERBOSE, + { + str1 = flingTypeToString(flingType) + bool1 = isClick + }, + { "QS fling with type $str1, originated from click: $isClick" } + ) + } + + private fun flingTypeToString(flingType: Int) = when (flingType) { + FLING_EXPAND -> "FLING_EXPAND" + FLING_COLLAPSE -> "FLING_COLLAPSE" + FLING_HIDE -> "FLING_HIDE" + else -> "UNKNOWN" + } + + fun logSplitShadeChanged(splitShadeEnabled: Boolean) { + buffer.log( + TAG, + LogLevel.VERBOSE, + { bool1 = splitShadeEnabled }, + { "Split shade state changed: split shade ${if (bool1) "enabled" else "disabled"}" } ) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt index 07820ecd513e..129d09ec3c14 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt @@ -43,8 +43,6 @@ constructor( shadeExpansionStateManager: ShadeExpansionStateManager, dumpManager: DumpManager, private val context: Context, - private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory, - private val noOpOverScroller: NoOpOverScroller, private val scrimShadeTransitionController: ScrimShadeTransitionController, private val statusBarStateController: SysuiStatusBarStateController, ) { @@ -57,17 +55,6 @@ constructor( private var currentPanelState: Int? = null private var lastShadeExpansionChangeEvent: ShadeExpansionChangeEvent? = null - private val splitShadeOverScroller by lazy { - splitShadeOverScrollerFactory.create({ qs }, { notificationStackScrollLayoutController }) - } - private val shadeOverScroller: ShadeOverScroller - get() = - if (inSplitShade && isScreenUnlocked() && propertiesInitialized()) { - splitShadeOverScroller - } else { - noOpOverScroller - } - init { updateResources() configurationController.addCallback( @@ -89,21 +76,14 @@ constructor( private fun onPanelStateChanged(@PanelState state: Int) { currentPanelState = state - shadeOverScroller.onPanelStateChanged(state) scrimShadeTransitionController.onPanelStateChanged(state) } private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { lastShadeExpansionChangeEvent = event - shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount) scrimShadeTransitionController.onPanelExpansionChanged(event) } - private fun propertiesInitialized() = - this::qs.isInitialized && - this::notificationPanelViewController.isInitialized && - this::notificationStackScrollLayoutController.isInitialized - private fun dump(pw: PrintWriter) { pw.println( """ diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt deleted file mode 100644 index f95125f5cb6c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shade.transition - -import android.animation.Animator -import android.animation.ValueAnimator -import android.content.Context -import android.content.res.Configuration -import android.util.MathUtils -import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.R -import com.android.systemui.animation.Interpolators -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.qs.QS -import com.android.systemui.shade.PanelState -import com.android.systemui.shade.STATE_CLOSED -import com.android.systemui.shade.STATE_OPENING -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.policy.ConfigurationController -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import java.io.PrintWriter - -class SplitShadeOverScroller -@AssistedInject -constructor( - configurationController: ConfigurationController, - dumpManager: DumpManager, - private val context: Context, - private val scrimController: ScrimController, - @Assisted private val qSProvider: () -> QS, - @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController -) : ShadeOverScroller { - - private var releaseOverScrollDuration = 0L - private var maxOverScrollAmount = 0 - private var previousOverscrollAmount = 0 - private var dragDownAmount: Float = 0f - @PanelState private var panelState: Int = STATE_CLOSED - - private var releaseOverScrollAnimator: Animator? = null - - private val qS: QS - get() = qSProvider() - - private val nsslController: NotificationStackScrollLayoutController - get() = nsslControllerProvider() - - init { - updateResources() - configurationController.addCallback( - object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - updateResources() - } - }) - dumpManager.registerCriticalDumpable("SplitShadeOverScroller") { printWriter, _ -> - dump(printWriter) - } - } - - private fun updateResources() { - val resources = context.resources - maxOverScrollAmount = resources.getDimensionPixelSize(R.dimen.shade_max_over_scroll_amount) - releaseOverScrollDuration = - resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong() - } - - override fun onPanelStateChanged(@PanelState newPanelState: Int) { - if (shouldReleaseOverscroll(previousState = panelState, newState = newPanelState)) { - releaseOverScroll() - } - panelState = newPanelState - } - - override fun onDragDownAmountChanged(newDragDownAmount: Float) { - if (dragDownAmount == newDragDownAmount) { - return - } - dragDownAmount = newDragDownAmount - if (shouldOverscroll()) { - overScroll(newDragDownAmount) - } - } - - private fun shouldOverscroll() = panelState == STATE_OPENING - - private fun shouldReleaseOverscroll(@PanelState previousState: Int, @PanelState newState: Int) = - previousState == STATE_OPENING && newState != STATE_OPENING - - private fun overScroll(dragDownAmount: Float) { - val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount) - applyOverscroll(overscrollAmount) - previousOverscrollAmount = overscrollAmount - } - - private fun calculateOverscrollAmount(dragDownAmount: Float): Int { - val fullHeight: Int = nsslController.height - val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight) - return (fullHeightProgress * maxOverScrollAmount).toInt() - } - - private fun applyOverscroll(overscrollAmount: Int) { - qS.setOverScrollAmount(overscrollAmount) - scrimController.setNotificationsOverScrollAmount(overscrollAmount) - nsslController.setOverScrollAmount(overscrollAmount) - } - - private fun releaseOverScroll() { - val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0) - animator.addUpdateListener { - val overScrollAmount = it.animatedValue as Int - qS.setOverScrollAmount(overScrollAmount) - scrimController.setNotificationsOverScrollAmount(overScrollAmount) - nsslController.setOverScrollAmount(overScrollAmount) - } - animator.interpolator = Interpolators.STANDARD - animator.duration = releaseOverScrollDuration - animator.start() - releaseOverScrollAnimator = animator - previousOverscrollAmount = 0 - } - - @VisibleForTesting - internal fun finishAnimations() { - releaseOverScrollAnimator?.end() - releaseOverScrollAnimator = null - } - - private fun dump(pw: PrintWriter) { - pw.println( - """ - SplitShadeOverScroller: - Resources: - releaseOverScrollDuration: $releaseOverScrollDuration - maxOverScrollAmount: $maxOverScrollAmount - State: - previousOverscrollAmount: $previousOverscrollAmount - dragDownAmount: $dragDownAmount - panelState: $panelState - """.trimIndent()) - } - - @AssistedFactory - fun interface Factory { - fun create( - qSProvider: () -> QS, - nsslControllerProvider: () -> NotificationStackScrollLayoutController - ): SplitShadeOverScroller - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index f20f929637cd..43fbc7cbae03 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -356,7 +356,7 @@ public final class KeyboardShortcuts { * Keyboard with its default map. */ private void retrieveKeyCharacterMap(int deviceId) { - final InputManager inputManager = InputManager.getInstance(); + final InputManager inputManager = mContext.getSystemService(InputManager.class); mBackupKeyCharacterMap = inputManager.getInputDevice(-1).getKeyCharacterMap(); if (deviceId != -1) { final InputDevice inputDevice = inputManager.getInputDevice(deviceId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b1152333b5e1..a127139fcc69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -66,6 +66,7 @@ import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInt import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.ShadeController; @@ -300,6 +301,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private KeyguardBypassController mBypassController; @Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI; + @Nullable private TaskbarDelegate mTaskbarDelegate; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -564,6 +566,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb updateStates(); } + public void setTaskbarDelegate(TaskbarDelegate taskbarDelegate) { + mTaskbarDelegate = taskbarDelegate; + } + /** * Show the keyguard. Will handle creating and attaching to the view manager * lazily. @@ -790,7 +796,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onFinishedGoingToSleep() { - mPrimaryBouncerInteractor.onScreenTurnedOff(); + mPrimaryBouncerInteractor.hide(); } @Override @@ -1193,7 +1199,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * Updates the visibility of the nav bar window (which will cause insets changes). */ protected void updateNavigationBarVisibility(boolean navBarVisible) { - if (mCentralSurfaces.getNavigationBarView() != null) { + if (mCentralSurfaces.getNavigationBarView() != null + || (mTaskbarDelegate != null && mTaskbarDelegate.isInitialized())) { if (navBarVisible) { long delay = getNavBarShowDelay(); if (delay == 0) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 568e8d20cd76..064bc9c0036d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -32,11 +32,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,7 +42,6 @@ import static org.mockito.Mockito.when; import android.content.res.Configuration; import android.content.res.Resources; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricSourceType; import android.media.AudioManager; import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; @@ -53,7 +50,6 @@ import android.testing.TestableResources; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.View; import android.view.WindowInsetsController; import android.widget.FrameLayout; @@ -96,10 +92,8 @@ import java.util.Optional; @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private static final int TARGET_USER_ID = 100; - @Rule public MockitoRule mRule = MockitoJUnit.rule(); - @Mock private KeyguardSecurityContainer mView; @Mock @@ -368,134 +362,12 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test - public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() { - setupConditionsToEnableSideFpsHint(); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - - verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - verify(mSideFpsController, never()).hide(any()); - } - - @Test - public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() { - setupConditionsToEnableSideFpsHint(); - setFingerprintDetectionRunning(false); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test - public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() { - setupConditionsToEnableSideFpsHint(); - setSideFpsHintEnabledFromResources(false); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test - public void onBouncerVisibilityChanged_unlockingWithFingerprintNotAllowed_sideFpsHintHidden() { - setupConditionsToEnableSideFpsHint(); - setUnlockingWithFingerprintAllowed(false); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test - public void onBouncerVisibilityChanged_sideFpsHintShown_sideFpsHintHidden() { - setupGetSecurityView(); - setupConditionsToEnableSideFpsHint(); - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test public void onBouncerVisibilityChanged_resetsScale() { - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE); - + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(false); verify(mView).resetScale(); } @Test - public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() { - setupGetSecurityView(); - setupConditionsToEnableSideFpsHint(); - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onStartingToHide(); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test - public void onPause_sideFpsHintShown_sideFpsHintHidden() { - setupGetSecurityView(); - setupConditionsToEnableSideFpsHint(); - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onPause(); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test - public void onResume_sideFpsHintShouldBeShown_sideFpsHintShown() { - setupGetSecurityView(); - setupConditionsToEnableSideFpsHint(); - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onResume(0); - - verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - verify(mSideFpsController, never()).hide(any()); - } - - @Test - public void onResume_sideFpsHintShouldNotBeShown_sideFpsHintHidden() { - setupGetSecurityView(); - setupConditionsToEnableSideFpsHint(); - setSideFpsHintEnabledFromResources(false); - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); - reset(mSideFpsController); - - mKeyguardSecurityContainerController.onResume(0); - - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - verify(mSideFpsController, never()).show(any(), anyInt()); - } - - @Test public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() { // GIVEN the current security method is SimPin when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false); @@ -721,39 +593,31 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { any(KeyguardSecurityCallback.class)); } + @Test + public void testSideFpsControllerShow() { + mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ true); + verify(mSideFpsController).show( + SideFpsUiRequestSource.PRIMARY_BOUNCER, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD); + } + + @Test + public void testSideFpsControllerHide() { + mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ false); + verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); + } + private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() { mKeyguardSecurityContainerController.onViewAttached(); verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture()); return mSwipeListenerArgumentCaptor.getValue(); } - private void setupConditionsToEnableSideFpsHint() { - attachView(); - setSideFpsHintEnabledFromResources(true); - setFingerprintDetectionRunning(true); - setUnlockingWithFingerprintAllowed(true); - } - private void attachView() { mKeyguardSecurityContainerController.onViewAttached(); verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); } - private void setFingerprintDetectionRunning(boolean running) { - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(running); - mKeyguardUpdateMonitorCallback.getValue().onBiometricRunningStateChanged(running, - BiometricSourceType.FINGERPRINT); - } - - private void setSideFpsHintEnabledFromResources(boolean enabled) { - mTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, - enabled); - } - - private void setUnlockingWithFingerprintAllowed(boolean allowed) { - when(mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()).thenReturn(allowed); - } - private void setupGetSecurityView() { when(mKeyguardSecurityViewFlipperController.getSecurityView( any(), any(KeyguardSecurityCallback.class))) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index e5d0692aaac2..a1e4f70352d6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -617,7 +617,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); - verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt()); + verify(mFingerprintManager, never()).detectFingerprint(any(), any(), any()); } @Test @@ -629,7 +629,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); - verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt()); + verify(mFingerprintManager, never()).detectFingerprint(any(), any(), any()); } @Test @@ -644,7 +644,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt()); - verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt()); + verify(mFingerprintManager).detectFingerprint(any(), any(), any()); } @Test @@ -733,7 +733,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testTriesToAuthenticate_whenBouncer() { setKeyguardBouncerVisibility(true); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); verify(mFaceManager, never()).hasEnrolledTemplates(anyInt()); } @@ -742,8 +742,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.sendPrimaryBouncerChanged( /* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -751,7 +750,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { keyguardIsVisible(); mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); verify(mUiEventLogger).logWithInstanceIdAndPosition( eq(FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP), eq(0), @@ -767,8 +766,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -779,8 +777,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -804,9 +801,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); // THEN face detect and authenticate are NOT triggered - verify(mFaceManager, never()).detectFace(any(), any(), anyInt()); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).detectFace(any(), any(), any()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); // THEN biometric help message sent to callback verify(keyguardUpdateMonitorCallback).onBiometricHelp( @@ -827,9 +823,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); // FACE detect is triggered, not authenticate - verify(mFaceManager).detectFace(any(), any(), anyInt()); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager).detectFace(any(), any(), any()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); // WHEN bouncer becomes visible setKeyguardBouncerVisibility(true); @@ -837,9 +832,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN face scanning is not run mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); - verify(mFaceManager, never()).detectFace(any(), any(), anyInt()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); + verify(mFaceManager, never()).detectFace(any(), any(), any()); } @Test @@ -854,9 +848,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); // FACE detect and authenticate are NOT triggered - verify(mFaceManager, never()).detectFace(any(), any(), anyInt()); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).detectFace(any(), any(), any()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -894,7 +887,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setKeyguardShowing(true, true); mKeyguardUpdateMonitor.setAssistantVisible(true); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -906,8 +899,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { any(), any(), any(), - anyInt(), - anyBoolean()); + anyInt()); } @Test @@ -938,7 +930,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>()); keyguardIsVisible(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -946,7 +938,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setKeyguardShowing(true, true); mKeyguardUpdateMonitor.setAssistantVisible(true); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); mTestableLooper.processAllMessages(); clearInvocations(mFaceManager); @@ -963,8 +955,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { any(), any(), any(), - anyInt(), - anyBoolean()); + anyInt()); } @Test @@ -974,8 +965,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */, KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>()); keyguardIsVisible(); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -987,9 +977,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { keyguardIsVisible(); mTestableLooper.processAllMessages(); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); - verify(mFaceManager, never()).detectFace(any(), any(), anyInt()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); + verify(mFaceManager, never()).detectFace(any(), any(), any()); } @Test @@ -1019,8 +1008,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { setKeyguardBouncerVisibility(true); mTestableLooper.processAllMessages(); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -1147,7 +1135,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); @@ -1599,8 +1587,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setCredentialAttempted(); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt()); - verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(), - anyBoolean()); + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -1975,7 +1962,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); @@ -2004,7 +1991,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mFaceManager, never()).authenticate( - any(), any(), any(), any(), anyInt(), anyBoolean()); + any(), any(), any(), any(), anyInt()); } @Test @@ -2018,14 +2005,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN face auth isn't triggered verify(mFaceManager, never()).authenticate( - any(), any(), any(), any(), anyInt(), anyBoolean()); + any(), any(), any(), any(), anyInt()); // WHEN device wakes up from the power button mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); // THEN face auth is triggered - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); } @Test @@ -2195,7 +2182,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); @@ -2228,7 +2215,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper.processAllMessages(); keyguardIsVisible(); - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; @@ -2596,8 +2583,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { any(), mAuthenticationCallbackCaptor.capture(), any(), - anyInt(), - anyBoolean()); + anyInt()); mAuthenticationCallbackCaptor.getValue() .onAuthenticationSucceeded( new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false)); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt index e9a2789bb5c8..9fe32f1e378b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt @@ -32,6 +32,7 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.Optional @@ -83,6 +84,33 @@ class ScreenOnCoordinatorTest : SysuiTestCase() { } @Test + fun testTasksReady_onScreenTurningOnAndTurnedOnEventsCalledTogether_callsDrawnCallback() { + screenOnCoordinator.onScreenTurningOn(runnable) + screenOnCoordinator.onScreenTurnedOn() + + onUnfoldOverlayReady() + onFoldAodReady() + waitHandlerIdle(testHandler) + + // Should be called when both unfold overlay and keyguard drawn ready + verify(runnable).run() + } + + @Test + fun testTasksReady_onScreenTurnedOnAndTurnedOffBeforeCompletion_doesNotCallDrawnCallback() { + screenOnCoordinator.onScreenTurningOn(runnable) + screenOnCoordinator.onScreenTurnedOn() + screenOnCoordinator.onScreenTurnedOff() + + onUnfoldOverlayReady() + onFoldAodReady() + waitHandlerIdle(testHandler) + + // Should not be called because this screen turning on call is not valid anymore + verify(runnable, never()).run() + } + + @Test fun testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback() { // Recreate with empty unfoldComponent screenOnCoordinator = ScreenOnCoordinator( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index c73ff1dab3d8..54c9d392ad1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -86,8 +86,9 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle mock(PrimaryBouncerCallbackInteractor::class.java), mock(FalsingCollector::class.java), mock(DismissCallbackRegistry::class.java), + context, + mKeyguardUpdateMonitor, mock(KeyguardBypassController::class.java), - mKeyguardUpdateMonitor ) mAlternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt new file mode 100644 index 000000000000..34d5661597ca --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt @@ -0,0 +1,93 @@ +/* + * 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.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import androidx.test.filters.SmallTest +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class DevicePolicyManagerExtTest { + + @Mock lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var userTracker: UserTracker + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(userTracker.userId).thenReturn(CURRENT_USER_ID) + } + + // region areKeyguardShortcutsDisabled + @Test + fun areKeyguardShortcutsDisabled_noDisabledKeyguardFeature_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_otherDisabledKeyguardFeatures_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SECURE_CAMERA or KEYGUARD_DISABLE_FACE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledShortcutsKeyguardFeature_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SHORTCUTS_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledAllKeyguardFeatures_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + // endregion + + private companion object { + const val CURRENT_USER_ID = 123 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt new file mode 100644 index 000000000000..f6ff4b214035 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.data.repository + +import android.hardware.input.InputManager +import android.view.InputDevice +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class KeyboardRepositoryTest : SysuiTestCase() { + + @Captor + private lateinit var deviceListenerCaptor: ArgumentCaptor<InputManager.InputDeviceListener> + @Mock private lateinit var inputManager: InputManager + + private lateinit var underTest: KeyboardRepository + private lateinit var dispatcher: CoroutineDispatcher + private lateinit var testScope: TestScope + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf()) + whenever(inputManager.getInputDevice(any())).then { invocation -> + val id = invocation.arguments.first() + INPUT_DEVICES_MAP[id] + } + dispatcher = StandardTestDispatcher() + testScope = TestScope(dispatcher) + underTest = KeyboardRepositoryImpl(testScope.backgroundScope, dispatcher, inputManager) + } + + @Test + fun emitsDisconnected_ifNothingIsConnected() = + testScope.runTest { + val initialState = underTest.keyboardConnected.first() + assertThat(initialState).isFalse() + } + + @Test + fun emitsConnected_ifKeyboardAlreadyConnectedAtTheStart() = + testScope.runTest { + whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(PHYSICAL_FULL_KEYBOARD_ID)) + val initialValue = underTest.keyboardConnected.first() + assertThat(initialValue).isTrue() + } + + @Test + fun emitsConnected_whenNewPhysicalKeyboardConnects() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceAdded(PHYSICAL_FULL_KEYBOARD_ID) + + assertThat(isKeyboardConnected).isTrue() + } + + @Test + fun emitsDisconnected_whenKeyboardDisconnects() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceAdded(PHYSICAL_FULL_KEYBOARD_ID) + assertThat(isKeyboardConnected).isTrue() + + deviceListener.onInputDeviceRemoved(PHYSICAL_FULL_KEYBOARD_ID) + assertThat(isKeyboardConnected).isFalse() + } + + private suspend fun captureDeviceListener(): InputManager.InputDeviceListener { + underTest.keyboardConnected.first() + verify(inputManager).registerInputDeviceListener(deviceListenerCaptor.capture(), nullable()) + return deviceListenerCaptor.value + } + + @Test + fun emitsDisconnected_whenVirtualOrNotFullKeyboardConnects() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceAdded(PHYSICAL_NOT_FULL_KEYBOARD_ID) + assertThat(isKeyboardConnected).isFalse() + + deviceListener.onInputDeviceAdded(VIRTUAL_FULL_KEYBOARD_ID) + assertThat(isKeyboardConnected).isFalse() + } + + @Test + fun emitsDisconnected_whenKeyboardDisconnectsAndWasAlreadyConnectedAtTheStart() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceRemoved(PHYSICAL_FULL_KEYBOARD_ID) + assertThat(isKeyboardConnected).isFalse() + } + + @Test + fun emitsConnected_whenAnotherDeviceDisconnects() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceAdded(PHYSICAL_FULL_KEYBOARD_ID) + deviceListener.onInputDeviceRemoved(VIRTUAL_FULL_KEYBOARD_ID) + + assertThat(isKeyboardConnected).isTrue() + } + + @Test + fun emitsConnected_whenOnePhysicalKeyboardDisconnectsButAnotherRemainsConnected() = + testScope.runTest { + val deviceListener = captureDeviceListener() + val isKeyboardConnected by collectLastValue(underTest.keyboardConnected) + + deviceListener.onInputDeviceAdded(PHYSICAL_FULL_KEYBOARD_ID) + deviceListener.onInputDeviceAdded(ANOTHER_PHYSICAL_FULL_KEYBOARD_ID) + deviceListener.onInputDeviceRemoved(ANOTHER_PHYSICAL_FULL_KEYBOARD_ID) + + assertThat(isKeyboardConnected).isTrue() + } + + @Test + fun passesKeyboardBacklightValues_fromBacklightListener() { + // TODO(b/268645734): implement when implementing backlight listener + } + + private companion object { + private const val PHYSICAL_FULL_KEYBOARD_ID = 1 + private const val VIRTUAL_FULL_KEYBOARD_ID = 2 + private const val PHYSICAL_NOT_FULL_KEYBOARD_ID = 3 + private const val ANOTHER_PHYSICAL_FULL_KEYBOARD_ID = 4 + + private val INPUT_DEVICES_MAP: Map<Int, InputDevice> = + mapOf( + PHYSICAL_FULL_KEYBOARD_ID to inputDevice(virtual = false, fullKeyboard = true), + VIRTUAL_FULL_KEYBOARD_ID to inputDevice(virtual = true, fullKeyboard = true), + PHYSICAL_NOT_FULL_KEYBOARD_ID to inputDevice(virtual = false, fullKeyboard = false), + ANOTHER_PHYSICAL_FULL_KEYBOARD_ID to + inputDevice(virtual = false, fullKeyboard = true) + ) + + private fun inputDevice(virtual: Boolean, fullKeyboard: Boolean): InputDevice = + mock<InputDevice>().also { + whenever(it.isVirtual).thenReturn(virtual) + whenever(it.isFullKeyboard).thenReturn(fullKeyboard) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt index 7c604f760681..d55370b20d09 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt @@ -21,6 +21,7 @@ import android.content.pm.UserInfo import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT import android.hardware.biometrics.ComponentInfoInternal +import android.hardware.face.FaceAuthenticateOptions import android.hardware.face.FaceManager import android.hardware.face.FaceSensorProperties import android.hardware.face.FaceSensorPropertiesInternal @@ -62,7 +63,6 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock @@ -276,7 +276,7 @@ class KeyguardFaceAuthManagerTest : SysuiTestCase() { underTest.detect() - verify(faceManager, never()).detectFace(any(), any(), anyInt()) + verify(faceManager, never()).detectFace(any(), any(), any()) } @Test @@ -379,7 +379,7 @@ class KeyguardFaceAuthManagerTest : SysuiTestCase() { .detectFace( cancellationSignal.capture(), detectionCallback.capture(), - eq(currentUserId) + eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()) ) } @@ -390,8 +390,7 @@ class KeyguardFaceAuthManagerTest : SysuiTestCase() { cancellationSignal.capture(), authenticationCallback.capture(), isNull(), - eq(currentUserId), - eq(true) + eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()) ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt index 46ed829e0574..6b7fd616e678 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -19,11 +19,13 @@ package com.android.systemui.keyguard.domain.interactor import android.os.Looper import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import android.testing.TestableResources import android.view.View import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.DejankUtils +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.keyguard.DismissCallbackRegistry @@ -69,6 +71,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private val mainHandler = FakeHandler(Looper.getMainLooper()) private lateinit var underTest: PrimaryBouncerInteractor + private lateinit var resources: TestableResources @Before fun setUp() { @@ -84,18 +87,19 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { mPrimaryBouncerCallbackInteractor, falsingCollector, dismissCallbackRegistry, - keyguardBypassController, + context, keyguardUpdateMonitor, + keyguardBypassController, ) `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) `when`(repository.primaryBouncerShow.value).thenReturn(null) `when`(bouncerView.delegate).thenReturn(bouncerViewDelegate) + resources = context.orCreateTestableResources } @Test fun testShow_isScrimmed() { underTest.show(true) - verify(repository).setOnScreenTurnedOff(false) verify(repository).setKeyguardAuthenticated(null) verify(repository).setPrimaryHide(false) verify(repository).setPrimaryStartingToHide(false) @@ -207,12 +211,6 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { } @Test - fun testOnScreenTurnedOff() { - underTest.onScreenTurnedOff() - verify(repository).setOnScreenTurnedOff(true) - } - - @Test fun testSetKeyguardPosition() { underTest.setKeyguardPosition(0f) verify(repository).setKeyguardPosition(0f) @@ -286,4 +284,98 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false) assertThat(underTest.willDismissWithAction()).isFalse() } + + @Test + fun testSideFpsVisibility() { + updateSideFpsVisibilityParameters( + isVisible = true, + sfpsEnabled = true, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true, + isAnimatingAway = false + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(true) + } + + @Test + fun testSideFpsVisibility_notVisible() { + updateSideFpsVisibilityParameters( + isVisible = false, + sfpsEnabled = true, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true, + isAnimatingAway = false + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(false) + } + + @Test + fun testSideFpsVisibility_sfpsNotEnabled() { + updateSideFpsVisibilityParameters( + isVisible = true, + sfpsEnabled = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true, + isAnimatingAway = false + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(false) + } + + @Test + fun testSideFpsVisibility_fpsDetectionNotRunning() { + updateSideFpsVisibilityParameters( + isVisible = true, + sfpsEnabled = true, + fpsDetectionRunning = false, + isUnlockingWithFpAllowed = true, + isAnimatingAway = false + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(false) + } + + @Test + fun testSideFpsVisibility_UnlockingWithFpNotAllowed() { + updateSideFpsVisibilityParameters( + isVisible = true, + sfpsEnabled = true, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = false, + isAnimatingAway = false + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(false) + } + + @Test + fun testSideFpsVisibility_AnimatingAway() { + updateSideFpsVisibilityParameters( + isVisible = true, + sfpsEnabled = true, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true, + isAnimatingAway = true + ) + underTest.updateSideFpsVisibility() + verify(repository).setSideFpsShowing(false) + } + + private fun updateSideFpsVisibilityParameters( + isVisible: Boolean, + sfpsEnabled: Boolean, + fpsDetectionRunning: Boolean, + isUnlockingWithFpAllowed: Boolean, + isAnimatingAway: Boolean + ) { + `when`(repository.primaryBouncerVisible.value).thenReturn(isVisible) + resources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, sfpsEnabled) + `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(fpsDetectionRunning) + `when`(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + .thenReturn(isUnlockingWithFpAllowed) + `when`(repository.primaryBouncerStartingDisappearAnimation.value) + .thenReturn(if (isAnimatingAway) Runnable {} else null) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index 75b74b0cfe28..f675e7997eb4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -26,7 +26,6 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.BouncerViewDelegate import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { private lateinit var repository: FakeKeyguardBouncerRepository @Mock private lateinit var bouncerView: BouncerView - @Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel @Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor @@ -69,8 +67,9 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { primaryBouncerCallbackInteractor, falsingCollector, dismissCallbackRegistry, - keyguardBypassController, + context, keyguardUpdateMonitor, + keyguardBypassController, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 586af626d29e..65e4c10265cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -16,15 +16,23 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.os.Looper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.runCurrent @@ -33,7 +41,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest @@ -41,31 +48,69 @@ import org.mockito.MockitoAnnotations @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardBouncerViewModelTest : SysuiTestCase() { lateinit var underTest: KeyguardBouncerViewModel + lateinit var bouncerInteractor: PrimaryBouncerInteractor @Mock lateinit var bouncerView: BouncerView - @Mock lateinit var bouncerInteractor: PrimaryBouncerInteractor + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel + @Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor + @Mock private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + private val mainHandler = FakeHandler(Looper.getMainLooper()) + val repository = FakeKeyguardBouncerRepository() @Before fun setup() { MockitoAnnotations.initMocks(this) + bouncerInteractor = + PrimaryBouncerInteractor( + repository, + bouncerView, + mainHandler, + keyguardStateController, + keyguardSecurityModel, + primaryBouncerCallbackInteractor, + falsingCollector, + dismissCallbackRegistry, + context, + keyguardUpdateMonitor, + keyguardBypassController, + ) underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) } @Test - fun setMessage() = - runTest { - val flow = MutableStateFlow<BouncerShowMessageModel?>(null) - var message: BouncerShowMessageModel? = null - Mockito.`when`(bouncerInteractor.showMessage) - .thenReturn(flow as Flow<BouncerShowMessageModel>) - // Reinitialize the view model. - underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) + fun setMessage() = runTest { + var message: BouncerShowMessageModel? = null + val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this) - flow.value = BouncerShowMessageModel(message = "abc", colorStateList = null) + repository.setShowMessage(BouncerShowMessageModel("abc", null)) + // Run the tasks that are pending at this point of virtual time. + runCurrent() + assertThat(message?.message).isEqualTo("abc") + job.cancel() + } + + @Test + fun shouldUpdateSideFps() = runTest { + var count = 0 + val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this) + repository.setPrimaryVisible(true) + // Run the tasks that are pending at this point of virtual time. + runCurrent() + assertThat(count).isEqualTo(1) + job.cancel() + } - val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this) - // Run the tasks that are pending at this point of virtual time. - runCurrent() - assertThat(message?.message).isEqualTo("abc") - job.cancel() - } + @Test + fun sideFpsShowing() = runTest { + var sideFpsIsShowing = false + val job = underTest.sideFpsShowing.onEach { sideFpsIsShowing = it }.launchIn(this) + repository.setSideFpsShowing(true) + // Run the tasks that are pending at this point of virtual time. + runCurrent() + assertThat(sideFpsIsShowing).isEqualTo(true) + job.cancel() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt index 537dfb821fef..1c9336a3fc66 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt @@ -13,6 +13,7 @@ import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.phone.LightBarController import com.android.systemui.statusbar.phone.LightBarTransitionsController +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.wm.shell.back.BackAnimation import com.android.wm.shell.pip.Pip import org.junit.Before @@ -66,6 +67,8 @@ class TaskbarDelegateTest : SysuiTestCase() { lateinit var mBackAnimation: BackAnimation @Mock lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState + @Mock + lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager @Before fun setup() { @@ -76,7 +79,7 @@ class TaskbarDelegateTest : SysuiTestCase() { `when`(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState) mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance() mTaskbarDelegate = TaskbarDelegate(context, mEdgeBackGestureHandlerFactory, - mLightBarControllerFactory) + mLightBarControllerFactory, mStatusBarKeyguardViewManager) mTaskbarDelegate.setDependencies(mCommandQueue, mOverviewProxyService, mNavBarHelper, mNavigationModeController, mSysUiState, mDumpManager, mAutoHideController, mLightBarController, mOptionalPip, mBackAnimation, mTaskStackChangeListeners) 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 39c4e06ff0bb..52b29ac909fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.Intent @@ -29,6 +30,7 @@ import com.android.systemui.notetask.NoteTaskController.Companion.INTENT_EXTRA_U import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent import com.android.systemui.notetask.NoteTaskInfoResolver.NoteTaskInfo import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq @@ -39,6 +41,7 @@ import java.util.Optional import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions @@ -64,6 +67,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() { @Mock lateinit var optionalUserManager: Optional<UserManager> @Mock lateinit var userManager: UserManager @Mock lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Before fun setUp() { @@ -75,6 +80,13 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager) whenever(optionalUserManager.orElse(null)).thenReturn(userManager) whenever(userManager.isUserUnlocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) } private fun createNoteTaskController(isEnabled: Boolean = true): NoteTaskController { @@ -84,8 +96,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { optionalBubbles = optionalBubbles, optionalKeyguardManager = optionalKeyguardManager, optionalUserManager = optionalUserManager, + devicePolicyManager = devicePolicyManager, isEnabled = isEnabled, uiEventLogger = uiEventLogger, + userTracker = userTracker, ) } @@ -291,6 +305,86 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } // endregion + // region keyguard policy + @Test + fun showNoteTask_keyguardLocked_keyguardDisableShortcutsAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null) + + verifyZeroInteractions(context, bubbles, uiEventLogger) + } + + @Test + fun showNoteTask_keyguardLocked_keyguardDisableFeaturesAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null) + + verifyZeroInteractions(context, bubbles, uiEventLogger) + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableShortcutsAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableFeaturesAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + // endregion + private companion object { const val NOTES_PACKAGE_NAME = "com.android.note.app" const val NOTES_UID = 123456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java index d828e510999a..515e1ee172ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java @@ -57,6 +57,7 @@ import com.android.systemui.settings.UserTracker; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -65,7 +66,6 @@ import org.mockito.MockitoAnnotations; import java.util.function.BiConsumer; - @RunWith(AndroidTestingRunner.class) public final class AppClipsActivityTest extends SysuiTestCase { @@ -140,6 +140,7 @@ public final class AppClipsActivityTest extends SysuiTestCase { mActivityRule.finishActivity(); } + @Ignore("b/269403503") @Test public void appClipsLaunched_screenshotDisplayed() { launchActivity(); @@ -147,6 +148,7 @@ public final class AppClipsActivityTest extends SysuiTestCase { assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull(); } + @Ignore("b/269403503") @Test public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() { ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> { @@ -166,6 +168,7 @@ public final class AppClipsActivityTest extends SysuiTestCase { verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_ACCEPTED, TEST_UID, TEST_CALLING_PACKAGE); } + @Ignore("b/269403503") @Test public void screenshotDisplayed_userDeclined() { ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt index 7cac854c0853..d5a1f804e6a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt @@ -2,37 +2,24 @@ package com.android.systemui.shade.transition import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.qs.QS -import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.shade.STATE_OPENING import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionStateManager -import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.FakeConfigurationController import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @SmallTest class ShadeTransitionControllerTest : SysuiTestCase() { - @Mock private lateinit var npvc: NotificationPanelViewController - @Mock private lateinit var nsslController: NotificationStackScrollLayoutController - @Mock private lateinit var qs: QS - @Mock private lateinit var noOpOverScroller: NoOpOverScroller - @Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @@ -52,119 +39,19 @@ class ShadeTransitionControllerTest : SysuiTestCase() { shadeExpansionStateManager, dumpManager, context, - splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller }, - noOpOverScroller, scrimShadeTransitionController, statusBarStateController, ) - - // Resetting as they are notified upon initialization. - reset(noOpOverScroller, splitShadeOverScroller) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_forwardsToSplitShadeOverScroller() { - initLateProperties() - enableSplitShade() - - startPanelExpansion() - - verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING) - verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(noOpOverScroller) - } - - @Test - fun onPanelStateChanged_inSplitShade_propertiesNotInitialized_forwardsToNoOpOverScroller() { - enableSplitShade() - - startPanelExpansion() - - verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) - verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(splitShadeOverScroller) - } - - @Test - fun onPanelStateChanged_inSplitShade_onKeyguard_forwardsToNoOpOverScroller() { - initLateProperties() - enableSplitShade() - setOnKeyguard() - - startPanelExpansion() - - verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) - verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(splitShadeOverScroller) - } - - @Test - fun onPanelStateChanged_inSplitShade_onLockedShade_forwardsToNoOpOverScroller() { - initLateProperties() - enableSplitShade() - setOnLockedShade() - - startPanelExpansion() - - verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) - verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(splitShadeOverScroller) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_onUnlockedShade_forwardsToSplitShadeOverScroller() { - initLateProperties() - enableSplitShade() - setOnUnlockedShade() - - startPanelExpansion() - - verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING) - verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(noOpOverScroller) - } - - @Test - fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() { - initLateProperties() - disableSplitShade() - - startPanelExpansion() - - verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) - verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) - verifyZeroInteractions(splitShadeOverScroller) } @Test fun onPanelStateChanged_forwardsToScrimTransitionController() { - initLateProperties() - startPanelExpansion() verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING) verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT) } - private fun initLateProperties() { - controller.qs = qs - controller.notificationStackScrollLayoutController = nsslController - controller.notificationPanelViewController = npvc - } - - private fun disableSplitShade() { - setSplitShadeEnabled(false) - } - - private fun enableSplitShade() { - setSplitShadeEnabled(true) - } - - private fun setSplitShadeEnabled(enabled: Boolean) { - overrideResource(R.bool.config_use_split_notification_shade, enabled) - configurationController.notifyConfigurationChanged() - } - private fun startPanelExpansion() { shadeExpansionStateManager.onPanelExpansionChanged( DEFAULT_EXPANSION_EVENT.fraction, @@ -174,23 +61,6 @@ class ShadeTransitionControllerTest : SysuiTestCase() { ) } - private fun setOnKeyguard() { - setShadeState(StatusBarState.KEYGUARD) - } - - private fun setOnLockedShade() { - setShadeState(StatusBarState.SHADE_LOCKED) - } - - private fun setOnUnlockedShade() { - setShadeState(StatusBarState.SHADE) - } - - private fun setShadeState(state: Int) { - whenever(statusBarStateController.state).thenReturn(state) - whenever(statusBarStateController.currentOrUpcomingState).thenReturn(state) - } - companion object { private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f private val DEFAULT_EXPANSION_EVENT = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt deleted file mode 100644 index 0e48b4835dfe..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.android.systemui.shade.transition - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.qs.QS -import com.android.systemui.shade.STATE_CLOSED -import com.android.systemui.shade.STATE_OPEN -import com.android.systemui.shade.STATE_OPENING -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.policy.FakeConfigurationController -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations - -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@SmallTest -class SplitShadeOverScrollerTest : SysuiTestCase() { - - @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var scrimController: ScrimController - @Mock private lateinit var qs: QS - @Mock private lateinit var nsslController: NotificationStackScrollLayoutController - - private val configurationController = FakeConfigurationController() - private lateinit var overScroller: SplitShadeOverScroller - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - whenever(nsslController.height).thenReturn(1000) - overScroller = - SplitShadeOverScroller( - configurationController, - dumpManager, - context, - scrimController, - { qs }, - { nsslController }) - } - - @Test - fun onDragDownAmountChanged_panelOpening_overScrolls_basedOnHeightAndMaxAmount() { - val maxOverScrollAmount = 50 - val dragDownAmount = 100f - overrideResource(R.dimen.shade_max_over_scroll_amount, maxOverScrollAmount) - configurationController.notifyConfigurationChanged() - - overScroller.onPanelStateChanged(STATE_OPENING) - overScroller.onDragDownAmountChanged(dragDownAmount) - - val expectedOverScrollAmount = - (dragDownAmount / nsslController.height * maxOverScrollAmount).toInt() - verify(qs).setOverScrollAmount(expectedOverScrollAmount) - verify(nsslController).setOverScrollAmount(expectedOverScrollAmount) - verify(scrimController).setNotificationsOverScrollAmount(expectedOverScrollAmount) - } - - @Test - fun onDragDownAmountChanged_panelClosed_doesNotOverScroll() { - overScroller.onPanelStateChanged(STATE_CLOSED) - overScroller.onDragDownAmountChanged(100f) - - verifyZeroInteractions(qs, scrimController, nsslController) - } - - @Test - fun onDragDownAmountChanged_panelOpen_doesNotOverScroll() { - overScroller.onPanelStateChanged(STATE_OPEN) - overScroller.onDragDownAmountChanged(100f) - - verifyZeroInteractions(qs, scrimController, nsslController) - } - - @Test - fun onPanelStateChanged_opening_thenOpen_releasesOverScroll() { - overScroller.onPanelStateChanged(STATE_OPENING) - overScroller.onDragDownAmountChanged(100f) - - overScroller.onPanelStateChanged(STATE_OPEN) - overScroller.finishAnimations() - - verify(qs, atLeastOnce()).setOverScrollAmount(0) - verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0) - verify(nsslController, atLeastOnce()).setOverScrollAmount(0) - } - - @Test - fun onPanelStateChanged_opening_thenClosed_releasesOverScroll() { - overScroller.onPanelStateChanged(STATE_OPENING) - overScroller.onDragDownAmountChanged(100f) - - overScroller.onPanelStateChanged(STATE_CLOSED) - overScroller.finishAnimations() - - verify(qs, atLeastOnce()).setOverScrollAmount(0) - verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0) - verify(nsslController, atLeastOnce()).setOverScrollAmount(0) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index d8446f4721b3..1aad83eb73ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -38,6 +38,8 @@ import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; +import android.view.WindowInsets; +import android.view.WindowInsetsController; import android.window.BackEvent; import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; @@ -65,8 +67,10 @@ import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInt import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -123,6 +127,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private BouncerView mBouncerView; @Mock private BouncerViewDelegate mBouncerViewDelegate; @Mock private OnBackAnimationCallback mBouncerViewDelegateBackCallback; + @Mock private NotificationShadeWindowView mNotificationShadeWindowView; + @Mock private WindowInsetsController mWindowInsetsController; + @Mock private TaskbarDelegate mTaskbarDelegate; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback @@ -151,6 +158,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM)) .thenReturn(true); + when(mCentralSurfaces.getNotificationShadeWindowView()) + .thenReturn(mNotificationShadeWindowView); + when(mNotificationShadeWindowView.getWindowInsetsController()) + .thenReturn(mWindowInsetsController); + mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager( getContext(), @@ -640,6 +652,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testHideTaskbar() { + when(mTaskbarDelegate.isInitialized()).thenReturn(true); + mStatusBarKeyguardViewManager.setTaskbarDelegate(mTaskbarDelegate); + mStatusBarKeyguardViewManager.updateNavigationBarVisibility(false); + verify(mWindowInsetsController).hide(WindowInsets.Type.navigationBars()); + } + + @Test public void hideAlternateBouncer_beforeCentralSurfacesRegistered() { mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt index 337421974562..9cdce20bbf1e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt @@ -21,6 +21,7 @@ import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.E import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [KeyguardRepository] */ @@ -44,8 +45,6 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { override val panelExpansionAmount = _panelExpansionAmount.asStateFlow() private val _keyguardPosition = MutableStateFlow(0f) override val keyguardPosition = _keyguardPosition.asStateFlow() - private val _onScreenTurnedOff = MutableStateFlow(false) - override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow() private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null) override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow() private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null) @@ -61,6 +60,8 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { override var lastAlternateBouncerVisibleTime: Long = 0L private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false) override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow() + private val _sideFpsShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) + override val sideFpsShowing: StateFlow<Boolean> = _sideFpsShowing.asStateFlow() override fun setPrimaryScrimmed(isScrimmed: Boolean) { _primaryBouncerScrimmed.value = isScrimmed @@ -122,7 +123,7 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { _isBackButtonEnabled.value = isBackButtonEnabled } - override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) { - _onScreenTurnedOff.value = onScreenTurnedOff + override fun setSideFpsShowing(isShowing: Boolean) { + _sideFpsShowing.value = isShowing } } diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index e6abc4c90fac..eba92305dd7b 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -335,7 +335,7 @@ public class SystemActionPerformer { KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD, null); - InputManager.getInstance() + mContext.getSystemService(InputManager.class) .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); event.recycle(); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 08ee6d76f284..61fc32d5fa15 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -169,6 +169,7 @@ public final class ContentCaptureManagerService extends @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs; @GuardedBy("mLock") int mDevCfgLogHistorySize; @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs; + @GuardedBy("mLock") boolean mDevCfgDisableFlushForViewTreeAppearing; private final Executor mDataShareExecutor = Executors.newCachedThreadPool(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -359,6 +360,8 @@ public final class ContentCaptureManagerService extends case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING: setFineTuneParamsFromDeviceConfig(); return; default: @@ -388,13 +391,20 @@ public final class ContentCaptureManagerService extends DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + mDevCfgDisableFlushForViewTreeAppearing = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + false); if (verbose) { Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): " + "bufferSize=" + mDevCfgMaxBufferSize + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs + ", logHistory=" + mDevCfgLogHistorySize - + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs); + + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs + + ", disableFlushForViewTreeAppearing=" + + mDevCfgDisableFlushForViewTreeAppearing); } } } @@ -629,6 +639,7 @@ public final class ContentCaptureManagerService extends } @Override // from AbstractMasterSystemService + @GuardedBy("mLock") protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); @@ -646,6 +657,8 @@ public final class ContentCaptureManagerService extends pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize); pw.print(prefix2); pw.print("idleUnbindTimeoutMs: "); pw.println(mDevCfgIdleUnbindTimeoutMs); + pw.print(prefix2); pw.print("disableFlushForViewTreeAppearing: "); + pw.println(mDevCfgDisableFlushForViewTreeAppearing); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); } @@ -1005,12 +1018,15 @@ public final class ContentCaptureManagerService extends return null; } - final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, - mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, - mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, - whitelistedComponents); - if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); - return options; + synchronized (mLock) { + final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, + mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, + mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, + mDevCfgDisableFlushForViewTreeAppearing, + whitelistedComponents); + if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); + return options; + } } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 57d28f95eb6a..dce88da6f111 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -75,7 +75,6 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; private final boolean mAllowBackgroundAuthentication; - private final boolean mIsKeyguardBypassEnabled; // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update // the state. We should think of a way to improve this in the future. @State @@ -95,7 +94,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, - boolean shouldVibrate, boolean isKeyguardBypassEnabled, int sensorStrength) { + boolean shouldVibrate, int sensorStrength) { super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, shouldVibrate, biometricLogger, biometricContext); mIsStrongBiometric = isStrongBiometric; @@ -107,7 +106,6 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> mLockoutTracker = lockoutTracker; mIsRestricted = restricted; mAllowBackgroundAuthentication = allowBackgroundAuthentication; - mIsKeyguardBypassEnabled = isKeyguardBypassEnabled; mShouldUseLockoutTracker = lockoutTracker != null; mSensorStrength = sensorStrength; } @@ -374,14 +372,6 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> return mState; } - /** - * @return true if the client supports bypass (e.g. passive auth such as face), and if it's - * enabled by the user. - */ - public boolean isKeyguardBypassEnabled() { - return mIsKeyguardBypassEnabled; - } - @Override public int getProtoEnum() { return BiometricsProto.CM_AUTHENTICATE; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index b2fd46d1475d..51829684f3ab 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -23,6 +23,7 @@ import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.SensorPropertiesInternal; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.IFaceService; import android.os.IBinder; import android.os.RemoteException; @@ -64,8 +65,11 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) throws RemoteException { mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId, - userId, sensorReceiver, opPackageName, requestId, cookie, - allowBackgroundAuthentication); + sensorReceiver, new FaceAuthenticateOptions.Builder() + .setUserId(userId) + .setOpPackageName(opPackageName) + .build(), + requestId, cookie, allowBackgroundAuthentication); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 0248010bc9f3..1ee9f53c5774 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -35,6 +35,7 @@ import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceServiceReceiver; import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; @@ -238,14 +239,15 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call - public long authenticate(final IBinder token, final long operationId, int userId, - final IFaceServiceReceiver receiver, final String opPackageName, - boolean isKeyguardBypassEnabled) { + public long authenticate(final IBinder token, final long operationId, + final IFaceServiceReceiver receiver, final FaceAuthenticateOptions options) { // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or // lockdown, something wrong happened. See similar path in FingerprintService. super.authenticate_enforcePermission(); + final int userId = options.getUserId(); + final String opPackageName = options.getOpPackageName(); final boolean restricted = false; // Face APIs are private final int statsClient = Utils.isKeyguard(getContext(), opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD @@ -261,18 +263,18 @@ public class FaceService extends SystemService { return -1; } - return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, - 0 /* cookie */, - new ClientMonitorCallbackConverter(receiver), opPackageName, restricted, - statsClient, isKeyguard, isKeyguardBypassEnabled); + return provider.second.scheduleAuthenticate(provider.first, token, operationId, + 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options, + restricted, statsClient, isKeyguard); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call - public long detectFace(final IBinder token, final int userId, - final IFaceServiceReceiver receiver, final String opPackageName) { + public long detectFace(final IBinder token, + final IFaceServiceReceiver receiver, final FaceAuthenticateOptions options) { super.detectFace_enforcePermission(); + final String opPackageName = options.getOpPackageName(); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "detectFace called from non-sysui package: " + opPackageName); return -1; @@ -284,7 +286,7 @@ public class FaceService extends SystemService { return -1; } - return provider.second.scheduleFaceDetect(provider.first, token, userId, + return provider.second.scheduleFaceDetect(provider.first, token, options.getUserId(), new ClientMonitorCallbackConverter(receiver), opPackageName, BiometricsProtoEnums.CLIENT_KEYGUARD); } @@ -292,9 +294,9 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void prepareForAuthentication(int sensorId, boolean requireConfirmation, - IBinder token, long operationId, int userId, - IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, - int cookie, boolean allowBackgroundAuthentication) { + IBinder token, long operationId, IBiometricSensorReceiver sensorReceiver, + FaceAuthenticateOptions options, long requestId, int cookie, + boolean allowBackgroundAuthentication) { super.prepareForAuthentication_enforcePermission(); final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); @@ -305,10 +307,10 @@ public class FaceService extends SystemService { final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients final boolean restricted = true; // BiometricPrompt is always restricted - provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie, - new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId, + provider.scheduleAuthenticate(sensorId, token, operationId, cookie, + new ClientMonitorCallbackConverter(sensorReceiver), options, requestId, restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - allowBackgroundAuthentication, isKeyguardBypassEnabled); + allowBackgroundAuthentication); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 85f95cec8377..609c6a77e50a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -22,6 +22,7 @@ import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; @@ -88,15 +89,15 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId); - long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, + long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled); + @NonNull FaceAuthenticateOptions options, + boolean restricted, int statsClient, boolean allowBackgroundAuthentication); - void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, + void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled); + @NonNull FaceAuthenticateOptions options, long requestId, + boolean restricted, int statsClient, boolean allowBackgroundAuthentication); void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index d8b825d2c0e5..29dd707a7c1d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -85,11 +85,11 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @NonNull UsageStats usageStats, @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication, - boolean isKeyguardBypassEnabled, @Authenticators.Types int sensorStrength) { + @Authenticators.Types int sensorStrength) { this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, isStrongBiometric, usageStats, lockoutCache /* lockoutCache */, - allowBackgroundAuthentication, isKeyguardBypassEnabled, + allowBackgroundAuthentication, context.getSystemService(SensorPrivacyManager.class), sensorStrength); } @@ -102,13 +102,13 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @NonNull UsageStats usageStats, @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication, - boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager, + SensorPrivacyManager sensorPrivacyManager, @Authenticators.Types int biometricStrength) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */, allowBackgroundAuthentication, false /* shouldVibrate */, - isKeyguardBypassEnabled, biometricStrength); + biometricStrength); setRequestId(requestId); mUsageStats = usageStats; mNotificationManager = context.getSystemService(NotificationManager.class); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 89852a14ba9b..41e02691ddf8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -32,6 +32,7 @@ import android.hardware.biometrics.common.ComponentInfo; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; @@ -435,21 +436,21 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, - int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull FaceAuthenticateOptions options, + long requestId, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication) { mHandler.post(() -> { + final int userId = options.getUserId(); final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FaceAuthenticationClient client = new FaceAuthenticationClient( mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback, - userId, operationId, restricted, opPackageName, cookie, + userId, operationId, restricted, options.getOpPackageName(), cookie, false /* requireConfirmation */, sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), mBiometricContext, isStrongBiometric, mUsageStats, mSensors.get(sensorId).getLockoutCache(), - allowBackgroundAuthentication, isKeyguardBypassEnabled, - Utils.getCurrentStrength(sensorId) - ); + allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId)); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @Override public void onClientStarted( @@ -470,14 +471,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, - int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication) { final long id = mRequestCounter.incrementAndGet(); - scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback, - opPackageName, id, restricted, statsClient, - allowBackgroundAuthentication, isKeyguardBypassEnabled); + scheduleAuthenticate(sensorId, token, operationId, cookie, callback, + options, id, restricted, statsClient, allowBackgroundAuthentication); return id; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 1adc5e3e74b5..7e575bc23f9c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -31,6 +31,7 @@ import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; @@ -665,19 +666,20 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, - int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, - @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + int cookie, @NonNull ClientMonitorCallbackConverter receiver, + @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted, + int statsClient, boolean allowBackgroundAuthentication) { mHandler.post(() -> { + final int userId = options.getUserId(); scheduleUpdateActiveUserWithoutHandler(userId); final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId); final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext, mLazyDaemon, token, requestId, receiver, userId, operationId, restricted, - opPackageName, cookie, false /* requireConfirmation */, mSensorId, + options.getOpPackageName(), cookie, false /* requireConfirmation */, mSensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), mBiometricContext, isStrongBiometric, mLockoutTracker, - mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled, + mUsageStats, allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId)); mScheduler.scheduleClientMonitor(client); }); @@ -685,14 +687,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, - int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, - @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + int cookie, @NonNull ClientMonitorCallbackConverter receiver, + @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication) { final long id = mRequestCounter.incrementAndGet(); - scheduleAuthenticate(sensorId, token, operationId, userId, cookie, receiver, - opPackageName, id, restricted, statsClient, - allowBackgroundAuthentication, isKeyguardBypassEnabled); + scheduleAuthenticate(sensorId, token, operationId, cookie, receiver, + options, id, restricted, statsClient, allowBackgroundAuthentication); return id; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index d4a7f085b8c5..1c1f56ccddd5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -72,12 +72,12 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats, boolean allowBackgroundAuthentication, - boolean isKeyguardBypassEnabled, @Authenticators.Types int sensorStrength) { + @Authenticators.Types int sensorStrength) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, isStrongBiometric, null /* taskStackListener */, lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */, - isKeyguardBypassEnabled, sensorStrength); + sensorStrength); setRequestId(requestId); mUsageStats = usageStats; mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 1c57151ed7bd..affc496edc70 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -45,6 +45,7 @@ import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintServiceReceiver; @@ -252,15 +253,15 @@ public class FingerprintService extends SystemService { public long authenticate( final IBinder token, final long operationId, - final int sensorId, - final int userId, final IFingerprintServiceReceiver receiver, - final String opPackageName, - final String attributionTag, - boolean ignoreEnrollmentState) { + final FingerprintAuthenticateOptions options) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); + final String opPackageName = options.getOpPackageName(); + final String attributionTag = options.getAttributionTag(); + final int userId = options.getUserId(); + final int sensorId = options.getSensorId(); if (!canUseFingerprint( opPackageName, @@ -314,7 +315,8 @@ public class FingerprintService extends SystemService { && sensorProps != null && sensorProps.isAnyUdfpsType()) { try { return authenticateWithPrompt(operationId, sensorProps, callingUid, - callingUserId, receiver, opPackageName, ignoreEnrollmentState); + callingUserId, receiver, opPackageName, + options.isIgnoreEnrollmentState()); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Invalid package", e); return -1; @@ -412,16 +414,18 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override - public long detectFingerprint(final IBinder token, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) { + public long detectFingerprint(final IBinder token, + final IFingerprintServiceReceiver receiver, + final FingerprintAuthenticateOptions options) { super.detectFingerprint_enforcePermission(); + final String opPackageName = options.getOpPackageName(); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName); return -1; } - if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { + if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, options.getUserId())) { // If this happens, something in KeyguardUpdateMonitor is wrong. This should only // ever be invoked when the user is encrypted or lockdown. Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown"); @@ -434,7 +438,7 @@ public class FingerprintService extends SystemService { return -1; } - return provider.second.scheduleFingerDetect(provider.first, token, userId, + return provider.second.scheduleFingerDetect(provider.first, token, options.getUserId(), new ClientMonitorCallbackConverter(receiver), opPackageName, BiometricsProtoEnums.CLIENT_KEYGUARD); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index a90679e755cf..d1a7b1339179 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -136,7 +136,6 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> null /* lockoutCache */, allowBackgroundAuthentication, false /* shouldVibrate */, - false /* isKeyguardBypassEnabled */, biometricStrength); setRequestId(requestId); mSensorOverlays = new SensorOverlays(udfpsOverlayController, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index a9cc897a2937..957005a9223e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -87,8 +87,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication, - false /* shouldVibrate */, false /* isKeyguardBypassEnabled */, - sensorStrength); + false /* shouldVibrate */, sensorStrength); setRequestId(requestId); mLockoutFrameworkImpl = lockoutTracker; mSensorOverlays = new SensorOverlays(udfpsOverlayController, diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 5d31e40ad9d3..ab2c002c2b24 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -258,7 +258,8 @@ public class Vpn { public static final int AUTOMATIC_KEEPALIVE_DELAY_SECONDS = 30; // Default keepalive timeout for carrier config is 5 minutes. Mimic this. - private static final int DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = 5 * 60; + @VisibleForTesting + static final int DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = 5 * 60; // TODO: create separate trackers for each unique VPN to support // automated reconnection diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 81e550e3d62f..6eb465e1049e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -24,6 +24,7 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; import static android.hardware.display.DisplayManager.EventsMask; +import static android.hardware.display.DisplayManager.HDR_OUTPUT_CONTROL_FLAG; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; @@ -38,8 +39,10 @@ import static android.hardware.display.DisplayManagerGlobal.DisplayEvent; import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; +import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.ROOT_UID; +import static android.provider.DeviceConfig.NAMESPACE_DISPLAY_MANAGER; import android.Manifest; import android.annotation.NonNull; @@ -137,6 +140,7 @@ import android.window.ScreenCapture; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; @@ -232,6 +236,9 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6; private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8; + private static final int[] EMPTY_ARRAY = new int[0]; + private static final HdrConversionMode HDR_CONVERSION_MODE_UNSUPPORTED = new HdrConversionMode( + HDR_CONVERSION_UNSUPPORTED); private final Context mContext; private final DisplayManagerHandler mHandler; @@ -250,6 +257,10 @@ public final class DisplayManagerService extends SystemService { @GuardedBy("mSyncRoot") private boolean mAreUserDisabledHdrTypesAllowed = true; + // This value indicates whether or not HDR output control is enabled. + // It is read from DeviceConfig and is updated via a listener if the config changes. + private volatile boolean mIsHdrOutputControlEnabled; + // Display mode chosen by user. private Display.Mode mUserPreferredMode; // HDR conversion mode chosen by user @@ -674,6 +685,11 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { mSafeMode = safeMode; mSystemReady = true; + mIsHdrOutputControlEnabled = isDeviceConfigHdrOutputControlEnabled(); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_DISPLAY_MANAGER, + BackgroundThread.getExecutor(), + properties -> mIsHdrOutputControlEnabled = + isDeviceConfigHdrOutputControlEnabled()); // Just in case the top inset changed before the system was ready. At this point, any // relevant configuration should be in place. recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)); @@ -681,7 +697,9 @@ public final class DisplayManagerService extends SystemService { updateSettingsLocked(); updateUserDisabledHdrTypesFromSettingsLocked(); updateUserPreferredDisplayModeSettingsLocked(); - updateHdrConversionModeSettingsLocked(); + if (mIsHdrOutputControlEnabled) { + updateHdrConversionModeSettingsLocked(); + } } mDisplayModeDirector.setDesiredDisplayModeSpecsListener( @@ -702,6 +720,12 @@ public final class DisplayManagerService extends SystemService { mContext.registerReceiver(mIdleModeReceiver, filter); } + private boolean isDeviceConfigHdrOutputControlEnabled() { + return DeviceConfig.getBoolean(NAMESPACE_DISPLAY_MANAGER, + HDR_OUTPUT_CONTROL_FLAG, + true); + } + @VisibleForTesting Handler getDisplayHandler() { return mHandler; @@ -2117,7 +2141,7 @@ public final class DisplayManagerService extends SystemService { private HdrConversionMode getHdrConversionModeSettingInternal() { if (!mInjector.getHdrOutputConversionSupport()) { - return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_UNSUPPORTED); + return HDR_CONVERSION_MODE_UNSUPPORTED; } synchronized (mSyncRoot) { if (mHdrConversionMode != null) { @@ -2129,7 +2153,7 @@ public final class DisplayManagerService extends SystemService { private HdrConversionMode getHdrConversionModeInternal() { if (!mInjector.getHdrOutputConversionSupport()) { - return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_UNSUPPORTED); + return HDR_CONVERSION_MODE_UNSUPPORTED; } HdrConversionMode mode; synchronized (mSyncRoot) { @@ -3948,6 +3972,9 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public void setHdrConversionMode(HdrConversionMode hdrConversionMode) { + if (!mIsHdrOutputControlEnabled) { + return; + } mContext.enforceCallingOrSelfPermission( Manifest.permission.MODIFY_HDR_CONVERSION_MODE, "Permission required to set the HDR conversion mode."); @@ -3961,6 +3988,9 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public HdrConversionMode getHdrConversionModeSetting() { + if (!mIsHdrOutputControlEnabled) { + return HDR_CONVERSION_MODE_UNSUPPORTED; + } final long token = Binder.clearCallingIdentity(); try { return getHdrConversionModeSettingInternal(); @@ -3971,6 +4001,9 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public HdrConversionMode getHdrConversionMode() { + if (!mIsHdrOutputControlEnabled) { + return HDR_CONVERSION_MODE_UNSUPPORTED; + } final long token = Binder.clearCallingIdentity(); try { return getHdrConversionModeInternal(); @@ -3982,6 +4015,9 @@ public final class DisplayManagerService extends SystemService { @Display.HdrCapabilities.HdrType @Override // Binder call public int[] getSupportedHdrOutputTypes() { + if (!mIsHdrOutputControlEnabled) { + return EMPTY_ARRAY; + } final long token = Binder.clearCallingIdentity(); try { return getSupportedHdrOutputTypesInternal(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 38cddc4c0127..0d394570ab8e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -229,7 +229,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.VibrationEffect; -import android.permission.PermissionCheckerManager; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.Settings; @@ -6774,7 +6773,7 @@ public class NotificationManagerService extends SystemService { final int permissionResult = mPermissionManager.checkPermissionForDataDelivery( Manifest.permission.USE_FULL_SCREEN_INTENT, source, /* message= */ null); - if (permissionResult != PermissionCheckerManager.PERMISSION_GRANTED) { + if (permissionResult != PermissionManager.PERMISSION_GRANTED) { makeStickyHun(notification); } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 69ea55969b03..c63cdddc8560 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -22,6 +22,7 @@ import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.util.StatsLog.ANNOTATION_ID_IS_UID; @@ -905,6 +906,9 @@ public class PreferencesHelper implements RankingConfig { Objects.requireNonNull(channel); Objects.requireNonNull(channel.getId()); Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())); + Preconditions.checkArgument(channel.getImportance() >= IMPORTANCE_NONE + && channel.getImportance() <= IMPORTANCE_MAX, "Invalid importance level"); + boolean needsPolicyFileChange = false, wasUndeleted = false, needsDndChange = false; synchronized (mPackagePreferences) { PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); @@ -993,11 +997,6 @@ public class PreferencesHelper implements RankingConfig { needsPolicyFileChange = true; - if (channel.getImportance() < IMPORTANCE_NONE - || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) { - throw new IllegalArgumentException("Invalid importance level"); - } - // Reset fields that apps aren't allowed to set. if (fromTargetApp && !hasDndAccess) { channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 96b37bf0b4cb..336ad75c5b2c 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -129,7 +129,6 @@ import android.media.AudioSystem; import android.media.IAudioService; import android.media.session.MediaSessionLegacyHelper; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.DeviceIdleManager; import android.os.FactoryTest; @@ -203,6 +202,7 @@ import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ArrayUtils; +import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; import com.android.server.ExtconStateObserver; import com.android.server.ExtconUEventObserver; @@ -416,6 +416,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { AppOpsManager mAppOpsManager; PackageManager mPackageManager; SideFpsEventHandler mSideFpsEventHandler; + LockPatternUtils mLockPatternUtils; private boolean mHasFeatureAuto; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; @@ -1074,8 +1075,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } synchronized (mLock) { - // Lock the device after the dream transition has finished. - mLockAfterAppTransitionFinished = true; + // If the setting to lock instantly on power button press is true, then set the flag to + // lock after the dream transition has finished. + mLockAfterAppTransitionFinished = + mLockPatternUtils.getPowerButtonInstantlyLocks(mCurrentUserId); } dreamManagerInternal.requestDream(); @@ -1993,6 +1996,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAccessibilityShortcutController = injector.getAccessibilityShortcutController( mContext, new Handler(), mCurrentUserId); mGlobalActionsFactory = injector.getGlobalActionsFactory(); + mLockPatternUtils = new LockPatternUtils(mContext); mLogger = new MetricsLogger(); mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 89b796836617..d21274a7b6ba 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5225,7 +5225,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Debug.getCallers(6)); // Before setting mVisibleRequested so we can track changes. - mTransitionController.collect(this); + boolean isCollecting = false; + if (mTransitionController.isShellTransitionsEnabled()) { + isCollecting = mTransitionController.isCollecting(); + if (isCollecting) { + mTransitionController.collect(this); + } else { + Slog.e(TAG, "setVisibility=" + visible + " while transition is not collecting " + + this + " caller=" + Debug.getCallers(8)); + } + } onChildVisibilityRequested(visible); @@ -5297,9 +5306,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Defer committing visibility until transition starts. - if (inTransition()) { - if (!visible && mTransitionController.inPlayingTransition(this) - && mTransitionController.isCollecting(this)) { + if (isCollecting) { + // It may be occluded by the activity above that calls convertFromTranslucent(). + if (!visible && mTransitionController.inPlayingTransition(this)) { mTransitionChangeFlags |= FLAG_IS_OCCLUDED; } return; @@ -5319,11 +5328,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Then its visibility will be committed until the transition is ready. */ private boolean deferCommitVisibilityChange(boolean visible) { + if (mTransitionController.isShellTransitionsEnabled()) { + // Shell transition doesn't use opening/closing sets. + return false; + } if (!mDisplayContent.mAppTransition.isTransitionSet()) { - if (mTransitionController.isShellTransitionsEnabled()) { - // Shell transition doesn't use opening/closing sets. - return false; - } // Defer committing visibility for non-home app which is animating by recents. if (isActivityTypeHome() || !isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) { return false; @@ -7978,6 +7987,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * @return The {@code true} if the current instance has {@link mCompatDisplayInsets} without + * considering the inheritance implemented in {@link #getCompatDisplayInsets()} + */ + boolean hasCompatDisplayInsetsWithoutInheritance() { + return mCompatDisplayInsets != null; + } + + /** * @return {@code true} if this activity is in size compatibility mode that uses the different * density than its parent or its bounds don't fit in parent naturally. */ @@ -7985,7 +8002,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mInSizeCompatModeForBounds) { return true; } - if (mCompatDisplayInsets == null || !shouldCreateCompatDisplayInsets() + if (getCompatDisplayInsets() == null || !shouldCreateCompatDisplayInsets() // The orientation is different from parent when transforming. || isFixedRotationTransforming()) { return false; @@ -8056,11 +8073,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void updateCompatDisplayInsets() { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - mCompatDisplayInsets = mLetterboxUiController.getInheritedCompatDisplayInsets(); - return; - } - if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) { + if (getCompatDisplayInsets() != null || !shouldCreateCompatDisplayInsets()) { // The override configuration is set only once in size compatibility mode. return; } @@ -8123,9 +8136,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override float getCompatScale() { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - return mLetterboxUiController.getInheritedSizeCompatScale(); - } return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale(); } @@ -8172,7 +8182,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolveFixedOrientationConfiguration(newParentConfiguration); } - if (mCompatDisplayInsets != null) { + if (getCompatDisplayInsets() != null) { resolveSizeCompatModeConfiguration(newParentConfiguration); } else if (inMultiWindowMode() && !isFixedOrientationLetterboxAllowed) { // We ignore activities' requested orientation in multi-window modes. They may be @@ -8190,7 +8200,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolveAspectRatioRestriction(newParentConfiguration); } - if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null + if (isFixedOrientationLetterboxAllowed || getCompatDisplayInsets() != null // In fullscreen, can be letterboxed for aspect ratio. || !inMultiWindowMode()) { updateResolvedBoundsPosition(newParentConfiguration); @@ -8198,7 +8208,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean isIgnoreOrientationRequest = mDisplayContent != null && mDisplayContent.getIgnoreOrientationRequest(); - if (mCompatDisplayInsets == null // for size compat mode set in updateCompatDisplayInsets + if (getCompatDisplayInsets() == null + // for size compat mode set in updateCompatDisplayInsets // Fixed orientation letterboxing is possible on both large screen devices // with ignoreOrientationRequest enabled and on phones in split screen even with // ignoreOrientationRequest disabled. @@ -8244,7 +8255,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.neverSandboxDisplayApis(sConstrainDisplayApisConfig), info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig), !matchParentBounds(), - mCompatDisplayInsets != null, + getCompatDisplayInsets() != null, shouldCreateCompatDisplayInsets()); } resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds); @@ -8551,8 +8562,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || orientationRespectedWithInsets)) { return; } + final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets(); - if (mCompatDisplayInsets != null && !mCompatDisplayInsets.mIsInFixedOrientationLetterbox) { + if (compatDisplayInsets != null && !compatDisplayInsets.mIsInFixedOrientationLetterbox) { // App prefers to keep its original size. // If the size compat is from previous fixed orientation letterboxing, we may want to // have fixed orientation letterbox again, otherwise it will show the size compat @@ -8607,9 +8619,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, containingBounds, desiredAspectRatio); - if (mCompatDisplayInsets != null) { - mCompatDisplayInsets.getBoundsByRotation( - mTmpBounds, newParentConfig.windowConfiguration.getRotation()); + if (compatDisplayInsets != null) { + compatDisplayInsets.getBoundsByRotation(mTmpBounds, + newParentConfig.windowConfiguration.getRotation()); if (resolvedBounds.width() != mTmpBounds.width() || resolvedBounds.height() != mTmpBounds.height()) { // The app shouldn't be resized, we only do fixed orientation letterboxing if the @@ -8623,7 +8635,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Calculate app bounds using fixed orientation bounds because they will be needed later // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(), - newParentConfig, mCompatDisplayInsets); + newParentConfig, compatDisplayInsets); mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds); } @@ -8680,13 +8692,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ? requestedOrientation // We should use the original orientation of the activity when possible to avoid // forcing the activity in the opposite orientation. - : mCompatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED - ? mCompatDisplayInsets.mOriginalRequestedOrientation + : getCompatDisplayInsets().mOriginalRequestedOrientation != ORIENTATION_UNDEFINED + ? getCompatDisplayInsets().mOriginalRequestedOrientation : newParentConfiguration.orientation; int rotation = newParentConfiguration.windowConfiguration.getRotation(); final boolean isFixedToUserRotation = mDisplayContent == null || mDisplayContent.getDisplayRotation().isFixedToUserRotation(); - if (!isFixedToUserRotation && !mCompatDisplayInsets.mIsFloating) { + if (!isFixedToUserRotation && !getCompatDisplayInsets().mIsFloating) { // Use parent rotation because the original display can be rotated. resolvedConfig.windowConfiguration.setRotation(rotation); } else { @@ -8702,11 +8714,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // rely on them to contain the original and unchanging width and height of the app. final Rect containingAppBounds = new Rect(); final Rect containingBounds = mTmpBounds; - mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation, + getCompatDisplayInsets().getContainerBounds(containingAppBounds, containingBounds, rotation, orientation, orientationRequested, isFixedToUserRotation); resolvedBounds.set(containingBounds); // The size of floating task is fixed (only swap), so the aspect ratio is already correct. - if (!mCompatDisplayInsets.mIsFloating) { + if (!getCompatDisplayInsets().mIsFloating) { mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); } @@ -8715,7 +8727,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // are calculated in compat container space. The actual position on screen will be applied // later, so the calculation is simpler that doesn't need to involve offset from parent. getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - mCompatDisplayInsets); + getCompatDisplayInsets()); // Use current screen layout as source because the size of app is independent to parent. resolvedConfig.screenLayout = computeScreenLayout( getConfiguration().screenLayout, resolvedConfig.screenWidthDp, @@ -8750,14 +8762,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Calculates the scale the size compatibility bounds into the region which is available // to application. - final int contentW = resolvedAppBounds.width(); - final int contentH = resolvedAppBounds.height(); - final int viewportW = containerAppBounds.width(); - final int viewportH = containerAppBounds.height(); final float lastSizeCompatScale = mSizeCompatScale; - // Only allow to scale down. - mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH) - ? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH); + updateSizeCompatScale(resolvedAppBounds, containerAppBounds); + final int containerTopInset = containerAppBounds.top - containerBounds.top; final boolean topNotAligned = containerTopInset != resolvedAppBounds.top - resolvedBounds.top; @@ -8797,6 +8804,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds); } + void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) { + // Only allow to scale down. + mSizeCompatScale = mLetterboxUiController.findOpaqueNotFinishingActivityBelow() + .map(activityRecord -> activityRecord.mSizeCompatScale) + .orElseGet(() -> { + final int contentW = resolvedAppBounds.width(); + final int contentH = resolvedAppBounds.height(); + final int viewportW = containerAppBounds.width(); + final int viewportH = containerAppBounds.height(); + return (contentW <= viewportW && contentH <= viewportH) ? 1f : Math.min( + (float) viewportW / contentW, (float) viewportH / contentH); + }); + } + private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) { if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity @@ -8859,10 +8880,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override public Rect getBounds() { - if (mSizeCompatBounds != null) { - return mSizeCompatBounds; - } - return super.getBounds(); + // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities + final Rect superBounds = super.getBounds(); + return mLetterboxUiController.findOpaqueNotFinishingActivityBelow() + .map(ActivityRecord::getBounds) + .orElseGet(() -> { + if (mSizeCompatBounds != null) { + return mSizeCompatBounds; + } + return superBounds; + }); } @Override @@ -8887,7 +8914,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it // will keep the same bounds and screen configuration when it was first launched regardless // how its parent window changes, so that the sandbox API will provide a consistent result. - if (mCompatDisplayInsets != null || shouldCreateCompatDisplayInsets()) { + if (getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()) { return true; } @@ -8929,7 +8956,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTransitionController.collect(this); } } - if (mCompatDisplayInsets != null) { + if (getCompatDisplayInsets() != null) { Configuration overrideConfig = getRequestedOverrideConfiguration(); // Adapt to changes in orientation locking. The app is still non-resizable, but // it can change which orientation is fixed. If the fixed orientation changes, @@ -9005,7 +9032,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mVisibleRequested) { // It may toggle the UI for user to restart the size compatibility mode activity. display.handleActivitySizeCompatModeIfNeeded(this); - } else if (mCompatDisplayInsets != null && !visibleIgnoringKeyguard + } else if (getCompatDisplayInsets() != null && !visibleIgnoringKeyguard && (app == null || !app.hasVisibleActivities())) { // visibleIgnoringKeyguard is checked to avoid clearing mCompatDisplayInsets during // displays change. Displays are turned off during the change so mVisibleRequested diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index dcafe80686ad..0dc6e0ff1054 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -217,6 +217,34 @@ class AsyncRotationController extends FadeAnimationController implements Consume if (DEBUG) Slog.d(TAG, "Requested to sync draw transaction"); } + /** + * If an async window is not requested to redraw or its surface is removed, then complete its + * operation directly to avoid waiting until timeout. + */ + void updateTargetWindows() { + if (mTransitionOp == OP_LEGACY || !mIsStartTransactionCommitted) return; + for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { + final Operation op = mTargetWindowTokens.valueAt(i); + if (op.mIsCompletionPending || op.mAction == Operation.ACTION_SEAMLESS) { + // Skip completed target. And seamless windows use the signal from blast sync. + continue; + } + final WindowToken token = mTargetWindowTokens.keyAt(i); + int readyCount = 0; + final int childCount = token.getChildCount(); + for (int j = childCount - 1; j >= 0; j--) { + final WindowState w = token.getChildAt(j); + // If the token no longer contains pending drawn windows, then it is ready. + if (w.isDrawn() || !w.mWinAnimator.getShown()) { + readyCount++; + } + } + if (readyCount == childCount) { + mDisplayContent.finishAsyncRotation(token); + } + } + } + /** Lets the window fit in new rotation naturally. */ private void finishOp(WindowToken windowToken) { final Operation op = mTargetWindowTokens.remove(windowToken); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ade2fe7152c0..17ec9cbd7428 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4761,6 +4761,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateWindowsForAnimator() { forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */); + if (mAsyncRotationController != null) { + mAsyncRotationController.updateTargetWindows(); + } } boolean isInputMethodClientFocus(int uid, int pid) { diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 513667024caa..fa49a6ba6c2b 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -22,6 +22,7 @@ import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT; import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS; import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY; +import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY; import android.annotation.IntDef; import android.annotation.NonNull; @@ -296,7 +297,6 @@ final class LetterboxConfiguration { R.bool.config_isCompatFakeFocusEnabled); mIsPolicyForIgnoringRequestedOrientationEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled); - mIsDisplayRotationImmersiveAppCompatPolicyEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled); mDeviceConfig.updateFlagActiveStatus( @@ -311,7 +311,9 @@ final class LetterboxConfiguration { mDeviceConfig.updateFlagActiveStatus( /* isActive */ mIsCompatFakeFocusEnabled, /* key */ KEY_ENABLE_COMPAT_FAKE_FOCUS); - + mDeviceConfig.updateFlagActiveStatus( + /* isActive */ mTranslucentLetterboxingEnabled, + /* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY); mLetterboxConfigurationPersister = letterboxConfigurationPersister; mLetterboxConfigurationPersister.start(); } @@ -1003,7 +1005,7 @@ final class LetterboxConfiguration { boolean isTranslucentLetterboxingEnabled() { return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled - && isTranslucentLetterboxingAllowed()); + && mDeviceConfig.getFlag(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY)); } void setTranslucentLetterboxingEnabled(boolean translucentLetterboxingEnabled) { @@ -1051,13 +1053,6 @@ final class LetterboxConfiguration { isDeviceInTabletopMode, nextVerticalPosition); } - // TODO(b/262378106): Cache a runtime flag and implement - // DeviceConfig.OnPropertiesChangedListener - static boolean isTranslucentLetterboxingAllowed() { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - "enable_translucent_activity_letterbox", false); - } - /** Whether fake sending focus is enabled for unfocused apps in splitscreen */ boolean isCompatFakeFocusEnabled() { return mIsCompatFakeFocusEnabled && mDeviceConfig.getFlag(KEY_ENABLE_COMPAT_FAKE_FOCUS); diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java index b364872e56e7..df3c8f0fdccc 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java +++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java @@ -48,6 +48,11 @@ final class LetterboxConfigurationDeviceConfig static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus"; private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true; + static final String KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = + "enable_letterbox_translucent_activity"; + + private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true; + @VisibleForTesting static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of( KEY_ENABLE_CAMERA_COMPAT_TREATMENT, @@ -57,7 +62,9 @@ final class LetterboxConfigurationDeviceConfig KEY_ALLOW_IGNORE_ORIENTATION_REQUEST, DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST, KEY_ENABLE_COMPAT_FAKE_FOCUS, - DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS + DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS, + KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, + DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY ); // Whether camera compatibility treatment is enabled. @@ -82,6 +89,10 @@ final class LetterboxConfigurationDeviceConfig // which isn't guaranteed by default in multi-window modes. private boolean mIsCompatFakeFocusAllowed = DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS; + // Whether the letterbox strategy for transparent activities is allowed + private boolean mIsTranslucentLetterboxingAllowed = + DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY; + // Set of active device configs that need to be updated in // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged. private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>(); @@ -129,6 +140,8 @@ final class LetterboxConfigurationDeviceConfig return mIsAllowIgnoreOrientationRequest; case KEY_ENABLE_COMPAT_FAKE_FOCUS: return mIsCompatFakeFocusAllowed; + case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY: + return mIsTranslucentLetterboxingAllowed; default: throw new AssertionError("Unexpected flag name: " + key); } @@ -141,20 +154,20 @@ final class LetterboxConfigurationDeviceConfig } switch (key) { case KEY_ENABLE_CAMERA_COMPAT_TREATMENT: - mIsCameraCompatTreatmentEnabled = - getDeviceConfig(key, defaultValue); + mIsCameraCompatTreatmentEnabled = getDeviceConfig(key, defaultValue); break; case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY: mIsDisplayRotationImmersiveAppCompatPolicyEnabled = getDeviceConfig(key, defaultValue); break; case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST: - mIsAllowIgnoreOrientationRequest = - getDeviceConfig(key, defaultValue); + mIsAllowIgnoreOrientationRequest = getDeviceConfig(key, defaultValue); break; case KEY_ENABLE_COMPAT_FAKE_FOCUS: - mIsCompatFakeFocusAllowed = - getDeviceConfig(key, defaultValue); + mIsCompatFakeFocusAllowed = getDeviceConfig(key, defaultValue); + break; + case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY: + mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue); break; default: throw new AssertionError("Unexpected flag name: " + key); diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index e3437683e957..e1dbe01aca61 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -193,12 +193,6 @@ final class LetterboxUiController { // The app compat state for the opaque activity if any private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - // If true it means that the opaque activity beneath a translucent one is in SizeCompatMode. - private boolean mIsInheritedInSizeCompatMode; - - // This is the SizeCompatScale of the opaque activity beneath a translucent one - private float mInheritedSizeCompatScale; - // The CompatDisplayInsets of the opaque activity beneath the translucent one. private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; @@ -735,8 +729,21 @@ final class LetterboxUiController { : mActivityRecord.inMultiWindowMode() ? mActivityRecord.getTask().getBounds() : mActivityRecord.getRootTask().getParent().getBounds(); + // In case of translucent activities an option is to use the WindowState#getFrame() of + // the first opaque activity beneath. In some cases (e.g. an opaque activity is using + // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct + // information and in particular it might provide a value for a smaller area making + // the letterbox overlap with the translucent activity's frame. + // If we use WindowState#getFrame() for the translucent activity's letterbox inner + // frame, the letterbox will then be overlapped with the translucent activity's frame. + // Because the surface layer of letterbox is lower than an activity window, this + // won't crop the content, but it may affect other features that rely on values stored + // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher + // For this reason we use ActivityRecord#getBounds() that the translucent activity + // inherits from the first opaque activity beneath and also takes care of the scaling + // in case of activities in size compat mode. final Rect innerFrame = hasInheritedLetterboxBehavior() - ? mActivityRecord.getWindowConfiguration().getBounds() : w.getFrame(); + ? mActivityRecord.getBounds() : w.getFrame(); mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint); } else if (mLetterbox != null) { mLetterbox.hide(); @@ -1386,10 +1393,10 @@ final class LetterboxUiController { mLetterboxConfigListener.onRemoved(); clearInheritedConfig(); } - // In case mActivityRecord.getCompatDisplayInsets() is not null we don't apply the + // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the // opaque activity constraints because we're expecting the activity is already letterboxed. - if (mActivityRecord.getTask() == null || mActivityRecord.getCompatDisplayInsets() != null - || mActivityRecord.fillsParent()) { + if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() + || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { return; } final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity( @@ -1417,6 +1424,7 @@ final class LetterboxUiController { // We need to initialize appBounds to avoid NPE. The actual value will // be set ahead when resolving the Configuration for the activity. mutatedConfiguration.windowConfiguration.setAppBounds(new Rect()); + inheritConfiguration(firstOpaqueActivityBeneath); return mutatedConfiguration; }); } @@ -1457,16 +1465,12 @@ final class LetterboxUiController { return mInheritedAppCompatState; } - float getInheritedSizeCompatScale() { - return mInheritedSizeCompatScale; - } - @Configuration.Orientation int getInheritedOrientation() { return mInheritedOrientation; } - public ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { + ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { return mInheritedCompatDisplayInsets; } @@ -1486,7 +1490,7 @@ final class LetterboxUiController { * @return The first not finishing opaque activity beneath the current translucent activity * if it exists and the strategy is enabled. */ - private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { + Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) { return Optional.empty(); } @@ -1508,8 +1512,6 @@ final class LetterboxUiController { } mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation(); mInheritedAppCompatState = firstOpaque.getAppCompatState(); - mIsInheritedInSizeCompatMode = firstOpaque.inSizeCompatMode(); - mInheritedSizeCompatScale = firstOpaque.getCompatScale(); mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets(); } @@ -1519,8 +1521,6 @@ final class LetterboxUiController { mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED; mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - mIsInheritedInSizeCompatMode = false; - mInheritedSizeCompatScale = 1f; mInheritedCompatDisplayInsets = null; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c7d855360df9..2af5460182dd 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3389,6 +3389,11 @@ class Task extends TaskFragment { && top.getOrganizedTask() == this && top.isState(RESUMED); // Whether the direct top activity is in size compat mode on foreground. info.topActivityInSizeCompat = isTopActivityResumed && top.inSizeCompatMode(); + if (info.topActivityInSizeCompat + && mWmService.mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) { + // We hide the restart button in case of transparent activities. + info.topActivityInSizeCompat = top.fillsParent(); + } // Whether the direct top activity is eligible for letterbox education. info.topActivityEligibleForLetterboxEducation = isTopActivityResumed && top.isEligibleForLetterboxEducation(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 41c112526787..98563f6e73fc 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7357,7 +7357,8 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.w(WM_ERROR, "unable to restore pointer icon"); } } else { - InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_DEFAULT); + mContext.getSystemService(InputManager.class) + .setPointerIconType(PointerIcon.TYPE_DEFAULT); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a102986832fe..e8625bc3d64b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -32,7 +32,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; -import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; @@ -424,7 +423,7 @@ class WindowStateAnimator { computeShownFrameLocked(); - if (w.isParentWindowHidden() || !w.isOnScreen()) { + if (!w.isOnScreen()) { hide(t, "prepareSurfaceLocked"); mWallpaperControllerLocked.hideWallpapers(w); @@ -449,30 +448,23 @@ class WindowStateAnimator { if (prepared && mDrawState == HAS_DRAWN) { if (mLastHidden) { - if (showSurfaceRobustlyLocked(t)) { - mAnimator.requestRemovalOfReplacedWindows(w); - mLastHidden = false; - final DisplayContent displayContent = w.getDisplayContent(); - if (!displayContent.getLastHasContent()) { - // This draw means the difference between unique content and mirroring. - // Run another pass through performLayout to set mHasContent in the - // LogicalDisplay. - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; - if (DEBUG_LAYOUT_REPEATS) { - mService.mWindowPlacerLocked.debugLayoutRepeats( - "showSurfaceRobustlyLocked " + w, - displayContent.pendingLayoutChanges); - } + mSurfaceController.showRobustly(t); + mAnimator.requestRemovalOfReplacedWindows(w); + mLastHidden = false; + final DisplayContent displayContent = w.getDisplayContent(); + if (!displayContent.getLastHasContent()) { + // This draw means the difference between unique content and mirroring. + // Run another pass through performLayout to set mHasContent in the + // LogicalDisplay. + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; + if (DEBUG_LAYOUT_REPEATS) { + mService.mWindowPlacerLocked.debugLayoutRepeats( + "showSurfaceRobustlyLocked " + w, + displayContent.pendingLayoutChanges); } - } else { - w.setOrientationChanging(false); } } } - } else { - if (mWin.isAnimating(TRANSITION | PARENTS)) { - ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this); - } } if (w.getOrientationChanging()) { @@ -511,22 +503,6 @@ class WindowStateAnimator { mSurfaceController.setColorSpaceAgnostic(mWin.getPendingTransaction(), agnostic); } - /** - * Have the surface flinger show a surface, robustly dealing with - * error conditions. In particular, if there is not enough memory - * to show the surface, then we will try to get rid of other surfaces - * in order to succeed. - * - * @return Returns true if the surface was successfully shown. - */ - private boolean showSurfaceRobustlyLocked(SurfaceControl.Transaction t) { - boolean shown = mSurfaceController.showRobustly(t); - if (!shown) - return false; - - return true; - } - void applyEnterAnimationLocked() { // If we are the new part of a window replacement transition and we have requested // not to animate, we instead want to make it seamless, so we don't want to apply diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 607ce251c4d1..33751b9f16a0 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -245,13 +245,13 @@ class WindowSurfaceController { t.setColorSpaceAgnostic(mSurfaceControl, agnostic); } - boolean showRobustly(SurfaceControl.Transaction t) { + void showRobustly(SurfaceControl.Transaction t) { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title); if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this + " during relayout"); if (mSurfaceShown) { - return true; + return; } setShown(true); @@ -262,7 +262,6 @@ class WindowSurfaceController { dc.mDisplayId, 1 /* request shown */, String.valueOf(dc.mWallpaperController.getWallpaperTarget())); } - return true; } boolean clearWindowContentFrameStats() { diff --git a/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java b/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java deleted file mode 100644 index f3feb0269598..000000000000 --- a/services/tests/mockingservicestests/src/android/hardware/face/FaceManagerTest.java +++ /dev/null @@ -1,86 +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 android.hardware.face; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.Looper; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(MockitoJUnitRunner.class) -public class FaceManagerTest { - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - Context mContext; - @Mock - IFaceService mService; - - @Captor - ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor; - - List<FaceSensorPropertiesInternal> mProps; - FaceManager mFaceManager; - - @Before - public void setUp() throws Exception { - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - mFaceManager = new FaceManager(mContext, mService); - mProps = new ArrayList<>(); - mProps.add(new FaceSensorPropertiesInternal( - 0 /* id */, - FaceSensorProperties.STRENGTH_STRONG, - 1 /* maxTemplatesAllowed */, - new ArrayList<>() /* conponentInfo */, - FaceSensorProperties.TYPE_UNKNOWN, - true /* supportsFaceDetection */, - true /* supportsSelfIllumination */, - false /* resetLockoutRequiresChallenge */)); - } - - @Test - public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException { - verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture()); - - mCaptor.getValue().onAllAuthenticatorsRegistered(mProps); - List<FaceSensorPropertiesInternal> actual = mFaceManager.getSensorPropertiesInternal(); - - assertThat(actual).isEqualTo(mProps); - verify(mService, never()).getSensorPropertiesInternal(any()); - } -} diff --git a/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java deleted file mode 100644 index 558202d51b27..000000000000 --- a/services/tests/mockingservicestests/src/android/hardware/fingerprint/FingerprintManagerTest.java +++ /dev/null @@ -1,88 +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 android.hardware.fingerprint; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.Looper; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; - -import java.util.ArrayList; -import java.util.List; - - -@RunWith(MockitoJUnitRunner.class) -public class FingerprintManagerTest { - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - Context mContext; - @Mock - IFingerprintService mService; - - @Captor - ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor; - - List<FingerprintSensorPropertiesInternal> mProps; - FingerprintManager mFingerprintManager; - - @Before - public void setUp() throws Exception { - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - mFingerprintManager = new FingerprintManager(mContext, mService); - mProps = new ArrayList<>(); - mProps.add(new FingerprintSensorPropertiesInternal( - 0 /* sensorId */, - FingerprintSensorProperties.STRENGTH_STRONG, - 1 /* maxEnrollmentsPerUser */, - new ArrayList<>() /* componentInfo */, - FingerprintSensorProperties.TYPE_UNKNOWN, - true /* halControlsIllumination */, - true /* resetLockoutRequiresHardwareAuthToken */, - new ArrayList<>() /* sensorLocations */)); - } - - @Test - public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException { - verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture()); - - mCaptor.getValue().onAllAuthenticatorsRegistered(mProps); - List<FingerprintSensorPropertiesInternal> actual = - mFingerprintManager.getSensorPropertiesInternal(); - - assertThat(actual).isEqualTo(mProps); - verify(mService, never()).getSensorPropertiesInternal(any()); - } -} diff --git a/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS b/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS index 3edcf70d7e67..6a2192a2c7fb 100644 --- a/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS +++ b/services/tests/mockingservicestests/src/android/hardware/fingerprint/OWNERS @@ -1 +1 @@ -include /services/core/java/com/android/server/biometrics/OWNERS
\ No newline at end of file +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index 3fd0e072809f..f8cfdf131a28 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -17,9 +17,12 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + import static com.android.server.am.ActivityManagerService.Injector; import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction; + import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -34,27 +37,30 @@ import android.os.Process; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.text.TextUtils; + import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.modules.utils.testing.TestableDeviceConfig; +import com.android.server.ExtendedMockitoTestCase; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerService; -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + import org.junit.After; import org.junit.Assume; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Tests for {@link CachedAppOptimizer}. @@ -63,10 +69,7 @@ import org.mockito.junit.MockitoJUnitRunner; * atest FrameworksMockingServicesTests:CachedAppOptimizerTest */ @Presubmit -@RunWith(MockitoJUnitRunner.class) -@Ignore("TODO(b/226641572): this test is broken and it cannot use ExtendedMockitoTestCase as it " - + "uses TestableDeviceConfigRule, which creates its own mockito session") -public final class CachedAppOptimizerTest { +public final class CachedAppOptimizerTest extends ExtendedMockitoTestCase { private ServiceThread mThread; @@ -84,16 +87,21 @@ public final class CachedAppOptimizerTest { @Mock private PackageManagerInternal mPackageManagerInt; - @Rule - public final TestableDeviceConfig.TestableDeviceConfigRule - mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + private final TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig(); + @Rule public final ApplicationExitInfoTest.ServiceThreadRule mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); + @Override + protected void initializeSession(StaticMockitoSessionBuilder builder) { + mDeviceConfig.setUpMockedClasses(builder); + } + @Before public void setUp() { System.loadLibrary("mockingservicestestjni"); + mDeviceConfig.setUpMockBehaviors(); mHandlerThread = new HandlerThread(""); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); @@ -124,6 +132,7 @@ public final class CachedAppOptimizerTest { mHandlerThread.quit(); mThread.quit(); mCountDown = null; + mDeviceConfig.tearDown(); } private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, String processName, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 4c898b0c4d58..b0c3a6e26b7a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -705,7 +705,7 @@ public class BiometricSchedulerTest { TEST_SENSOR_ID, mock(BiometricLogger.class), biometricContext, true /* isStrongBiometric */, null /* taskStackListener */, null /* lockoutTracker */, false /* isKeyguard */, - true /* shouldVibrate */, false /* isKeyguardBypassEnabled */, + true /* shouldVibrate */, 0 /* sensorStrength */); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index 139910ba8926..184a5562a689 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -159,7 +159,7 @@ public class FaceAuthenticationClientTest { false /* requireConfirmation */, 9 /* sensorId */, mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, mUsageStats, null /* mLockoutCache */, false /* allowBackgroundAuthentication */, - false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */, + null /* sensorPrivacyManager */, 0 /* biometricStrength */) { @Override protected ActivityTaskManager getActivityTaskManager() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index ef3b007171b2..f08d0f5f71a4 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -177,7 +177,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.permission.PermissionCheckerManager; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.MediaStore; @@ -10254,7 +10253,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionCheckerManager.PERMISSION_HARD_DENIED, + /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, /* isSticky= */ true); } @@ -10263,7 +10262,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionCheckerManager.PERMISSION_SOFT_DENIED, + /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, /* isSticky= */ true); } @@ -10272,7 +10271,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionCheckerManager.PERMISSION_GRANTED, + /* permissionState= */ PermissionManager.PERMISSION_GRANTED, /* isSticky= */ false); } @@ -10281,7 +10280,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionCheckerManager.PERMISSION_HARD_DENIED, + /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, /* isSticky= */ true); } @@ -10290,7 +10289,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionCheckerManager.PERMISSION_SOFT_DENIED, + /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, /* isSticky= */ true); } @@ -10299,7 +10298,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionCheckerManager.PERMISSION_GRANTED, + /* permissionState= */ PermissionManager.PERMISSION_GRANTED, /* isSticky= */ true); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index f0f9204c4e4e..1ecd4a1ffd7e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -57,6 +57,7 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -1576,6 +1577,47 @@ public class PreferencesHelperTest extends UiServiceTestCase { new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false)); } + @Test + public void testUpdateChannel_downgradeImportance() { + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_DEFAULT), + true, false); + + assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false)); + } + + @Test + public void testUpdateChannel_upgradeImportance_ignored() { + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_DEFAULT), + true, false); + + assertFalse(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false)); + } + + @Test + public void testUpdateChannel_badImportance() { + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_DEFAULT), + true, false); + + assertThrows(IllegalArgumentException.class, + () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1), true, + false)); + + assertThrows(IllegalArgumentException.class, + () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED), true, + false)); + + assertThrows(IllegalArgumentException.class, + () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, + new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1), true, + false)); + } @Test public void testUpdate() throws Exception { diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index c9f758ce1f55..e9aca5666a93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -279,7 +279,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testTranslucentActivitiesWhenUnfolding() { mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); setUpDisplaySizeWithApp(2800, 1400); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mActivity.mDisplayContent.setIgnoreOrientationRequest( + true /* ignoreOrientationRequest */); mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( 1.0f /*letterboxVerticalPositionMultiplier*/); prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); @@ -290,18 +291,23 @@ public class SizeCompatTests extends WindowTestsBase { .build(); doReturn(false).when(translucentActivity).fillsParent(); mTask.addChild(translucentActivity); + assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); spyOn(mActivity); // Halffold - setFoldablePosture(translucentActivity, true /* isHalfFolded */, false /* isTabletop */); + setFoldablePosture(translucentActivity, true /* isHalfFolded */, + false /* isTabletop */); verify(mActivity).recomputeConfiguration(); + assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); clearInvocations(mActivity); // Unfold - setFoldablePosture(translucentActivity, false /* isHalfFolded */, false /* isTabletop */); + setFoldablePosture(translucentActivity, false /* isHalfFolded */, + false /* isTabletop */); verify(mActivity).recomputeConfiguration(); + assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 05ee2f24fbaf..3f14217b7a18 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1018,6 +1018,10 @@ public class TransitionTests extends WindowTestsBase { @Test public void testDisplayRotationChange() { + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + spyOn(displayPolicy); + // Simulate gesture navigation (non-movable) so it is not seamless. + doReturn(false).when(displayPolicy).navigationBarCanMove(); final Task task = createActivityRecord(mDisplayContent).getTask(); final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); @@ -1072,7 +1076,8 @@ public class TransitionTests extends WindowTestsBase { // Navigation bar finishes drawing after the start transaction, so its fade-in animation // can execute directly. - asyncRotationController.handleFinishDrawing(navBar, mMockT); + navBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; + asyncRotationController.updateTargetWindows(); assertFalse(asyncRotationController.isTargetToken(navBar.mToken)); assertNull(mDisplayContent.getAsyncRotationController()); } diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index 2135e276e591..6b2bea032a00 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -147,10 +147,8 @@ public final class CallControl { * <li>{@link DisconnectCause#REJECTED}</li> * <li>{@link DisconnectCause#MISSED}</li> * </ul> - * * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback * will be called on. - * * @param callback That will be completed on the Telecom side that details success or * failure of the requested operation. * @@ -254,6 +252,36 @@ public final class CallControl { } /** + * Raises an event to the {@link android.telecom.InCallService} implementations tracking this + * call via {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}. + * These events and the associated extra keys for the {@code Bundle} parameter are defined + * in Android X. This API is used to relay additional information about a call other than + * what is specified in the {@link android.telecom.CallAttributes} to + * {@link android.telecom.InCallService}s. This might include, for example, a change to the list + * of participants in a meeting, or the name of the speakers who have their hand raised. Where + * appropriate, the {@link InCallService}s tracking this call may choose to render this + * additional information about the call. An automotive calling UX, for example may have enough + * screen real estate to indicate the number of participants in a meeting, but to prevent + * distractions could suppress the list of participants. + * + * @param event that is defined in AndroidX (ex. The number of participants changed) + * @param extras the updated value in relation to the event (ex. 4 participants) + */ + public void sendEvent(@NonNull String event, @NonNull Bundle extras) { + Objects.requireNonNull(event); + Objects.requireNonNull(extras); + if (mServerInterface != null) { + try { + mServerInterface.sendEvent(mCallId, event, extras); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } else { + throw new IllegalStateException(INTERFACE_ERROR_MSG); + } + } + + /** * Since {@link OutcomeReceiver}s cannot be passed via AIDL, a ResultReceiver (which can) must * wrap the Clients {@link OutcomeReceiver} passed in and await for the Telecom Server side * response in {@link ResultReceiver#onReceiveResult(int, Bundle)}. diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java index bfe368560a22..d96c406c4294 100644 --- a/telecomm/java/android/telecom/CallEventCallback.java +++ b/telecomm/java/android/telecom/CallEventCallback.java @@ -17,6 +17,7 @@ package android.telecom; import android.annotation.NonNull; +import android.os.Bundle; import java.util.List; @@ -56,4 +57,17 @@ public interface CallEventCallback { * @param reason Code to indicate the reason of this failure */ void onCallStreamingFailed(@CallStreamingService.StreamingFailedReason int reason); + + /** + * Informs this {@link android.telecom.CallEventCallback} on events raised from a + * {@link android.telecom.InCallService} presenting this call. The event key and extra values + * are defined in AndroidX. This enables alternative calling surfaces, such as an automotive + * UI, to relay requests to perform other non-standard call actions to the app. For example, + * an automotive calling solution may offer the ability for the user to raise their hand + * during a meeting. + * + * @param event that is defined in AndroidX (ex. the number of participants changed) + * @param extras the updated value in relation to the event (ex. 4 participants) + */ + void onEvent(@NonNull String event, @NonNull Bundle extras); } diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java index 7bba1eb07ebd..e44e2b31c189 100644 --- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java +++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java @@ -19,6 +19,7 @@ package com.android.internal.telecom; import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS; import android.os.Binder; +import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.ResultReceiver; import android.telecom.CallAttributes; @@ -148,6 +149,7 @@ public class ClientTransactionalServiceWrapper { private static final String ON_AVAILABLE_CALL_ENDPOINTS = "onAvailableCallEndpointsChanged"; private static final String ON_MUTE_STATE_CHANGED = "onMuteStateChanged"; private static final String ON_CALL_STREAMING_FAILED = "onCallStreamingFailed"; + private static final String ON_EVENT = "onEvent"; private void handleHandshakeCallback(String action, String callId, int code, ResultReceiver ackResultReceiver) { @@ -314,5 +316,23 @@ public class ClientTransactionalServiceWrapper { Log.i(TAG, TextUtils.formatSimple("oCSF: id=[%s], reason=[%s]", callId, reason)); handleEventCallback(callId, ON_CALL_STREAMING_FAILED, reason); } + + @Override + public void onEvent(String callId, String event, Bundle extras) { + // lookup the callEventCallback associated with the particular call + TransactionalCall call = mCallIdToTransactionalCall.get(callId); + if (call != null) { + CallEventCallback callback = call.getCallStateCallback(); + Executor executor = call.getExecutor(); + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + callback.onEvent(event, extras); + }); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } }; } diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl index b78a77ec9756..3e651e92e612 100644 --- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl @@ -16,6 +16,7 @@ package com.android.internal.telecom; +import android.os.Bundle; import android.telecom.CallControl; import android.telecom.CallEndpoint; import android.telecom.DisconnectCause; @@ -30,4 +31,5 @@ oneway interface ICallControl { void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback); void startCallStreaming(String callId, in ResultReceiver callback); void requestCallEndpointChange(in CallEndpoint callEndpoint, in ResultReceiver callback); + void sendEvent(String callId, String event, in Bundle extras); }
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl index fef5e9e47dc5..dd61d173ef7e 100644 --- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl @@ -16,6 +16,7 @@ package com.android.internal.telecom; +import android.os.Bundle; import android.telecom.CallControl; import android.telecom.CallEndpoint; import com.android.internal.telecom.ICallControl; @@ -44,6 +45,8 @@ oneway interface ICallEventCallback { void onCallEndpointChanged(String callId, in CallEndpoint endpoint); void onAvailableCallEndpointsChanged(String callId, in List<CallEndpoint> endpoint); void onMuteStateChanged(String callId, boolean isMuted); + // -- Events + void onEvent(String callId, String event, in Bundle extras); // hidden methods that help with cleanup void removeCallFromTransactionalServiceWrapper(String callId); }
\ No newline at end of file diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index 90ddb6ffb34a..d2a6bf288be4 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -31,6 +31,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -299,6 +300,8 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions downgrades unused packages. @Test + @Ignore("b/251438180: This test has been failing for a long time; temporarily disable it while" + + " we investigate this issue.") public void testBackgroundDexOptDowngradeSuccessful() throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; diff --git a/tests/SilkFX/assets/gainmaps/granddam.jpg b/tests/SilkFX/assets/gainmaps/granddam.jpg Binary files differnew file mode 100644 index 000000000000..823f14efcbb2 --- /dev/null +++ b/tests/SilkFX/assets/gainmaps/granddam.jpg diff --git a/tests/SilkFX/assets/gainmaps/lightbulb.jpg b/tests/SilkFX/assets/gainmaps/lightbulb.jpg Binary files differnew file mode 100644 index 000000000000..232c5f0937df --- /dev/null +++ b/tests/SilkFX/assets/gainmaps/lightbulb.jpg diff --git a/tests/SilkFX/assets/gainmaps/porsche911.jpg b/tests/SilkFX/assets/gainmaps/porsche911.jpg Binary files differnew file mode 100644 index 000000000000..50f4fc83910e --- /dev/null +++ b/tests/SilkFX/assets/gainmaps/porsche911.jpg |