Merge "Add NoHyphenationSpan and no hyphenation config into LineBreakConfig" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 6bd6c93..94b3a10 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -19,6 +19,7 @@
// Add java_aconfig_libraries to here to add them to the core framework
srcs: [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+ ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
@@ -31,6 +32,7 @@
":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
":android.widget.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
],
// Add aconfig-annotations-lib as a dependency for the optimization
libs: ["aconfig-annotations-lib"],
@@ -214,3 +216,28 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Package Manager
+aconfig_declarations {
+ name: "android.content.pm.flags-aconfig",
+ package: "android.content.pm",
+ srcs: ["core/java/android/content/pm/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.content.pm.flags-aconfig-java",
+ aconfig_declarations: "android.content.pm.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Media BetterTogether
+aconfig_declarations {
+ name: "com.android.media.flags.bettertogether-aconfig",
+ package: "com.android.media.flags",
+ srcs: ["media/java/android/media/flags/media_better_together.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.media.flags.bettertogether-aconfig-java",
+ aconfig_declarations: "com.android.media.flags.bettertogether-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 66c1efc..24d815f 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -420,7 +420,7 @@
public static final int REASON_SYSTEM_EXEMPT_APP_OP = 327;
/**
- * Granted by {@link com.android.server.pm.PackageArchiverService} to the installer responsible
+ * Granted by {@link com.android.server.pm.PackageArchiver} to the installer responsible
* for unarchiving an app.
*
* @hide
diff --git a/core/api/current.txt b/core/api/current.txt
index d2ff81b0..1879da5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -23951,10 +23951,12 @@
method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
+ method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) @Nullable public android.media.RouteListingPreference getRouteListingPreference();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
method @NonNull public android.media.MediaRouter2.RoutingController getSystemController();
method public void registerControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.ControllerCallback);
method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
+ method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void registerRouteListingPreferenceCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
@@ -23963,6 +23965,7 @@
method public void transferTo(@NonNull android.media.MediaRoute2Info);
method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback);
+ method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void unregisterRouteListingPreferenceCallback(@NonNull android.media.MediaRouter2.RouteListingPreferenceCallback);
method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback);
}
@@ -23983,6 +23986,11 @@
method public void onRoutesUpdated(@NonNull java.util.List<android.media.MediaRoute2Info>);
}
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public abstract static class MediaRouter2.RouteListingPreferenceCallback {
+ ctor @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public MediaRouter2.RouteListingPreferenceCallback();
+ method @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2) public void onRouteListingPreferenceChanged(@Nullable android.media.RouteListingPreference);
+ }
+
public class MediaRouter2.RoutingController {
method public void deselectRoute(@NonNull android.media.MediaRoute2Info);
method @Nullable public android.os.Bundle getControlHints();
@@ -46578,9 +46586,9 @@
}
public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
- ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
- ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
- ctor @Deprecated public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
+ ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+ ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+ ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public void ellipsized(int, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
@@ -46593,12 +46601,12 @@
method public int getLineTop(int);
method public int getParagraphDirection(int);
method public int getTopPadding();
- method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
- method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
- method @Deprecated @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
- method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
- method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
- method @Deprecated @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
+ method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
+ method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
+ method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
+ method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+ method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+ method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
@@ -46606,7 +46614,7 @@
public static class BoringLayout.Metrics extends android.graphics.Paint.FontMetricsInt {
ctor public BoringLayout.Metrics();
- method @NonNull public android.graphics.RectF getDrawingBoundingBox();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF getDrawingBoundingBox();
field public int width;
}
@@ -46791,7 +46799,7 @@
public abstract class Layout {
ctor protected Layout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float);
- method @NonNull public android.graphics.RectF computeDrawingBoundingBox();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.RectF computeDrawingBoundingBox();
method public void draw(android.graphics.Canvas);
method public void draw(android.graphics.Canvas, android.graphics.Path, android.graphics.Paint, int);
method public void draw(@NonNull android.graphics.Canvas, @Nullable java.util.List<android.graphics.Path>, @Nullable java.util.List<android.graphics.Paint>, @Nullable android.graphics.Path, @Nullable android.graphics.Paint, int);
@@ -46800,24 +46808,24 @@
method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
method @NonNull public final android.text.Layout.Alignment getAlignment();
method public abstract int getBottomPadding();
- method public final int getBreakStrategy();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getBreakStrategy();
method public void getCursorPath(int, android.graphics.Path, CharSequence);
method public static float getDesiredWidth(CharSequence, android.text.TextPaint);
method public static float getDesiredWidth(CharSequence, int, int, android.text.TextPaint);
method public abstract int getEllipsisCount(int);
method public abstract int getEllipsisStart(int);
- method @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
method @IntRange(from=0) public int getEllipsizedWidth();
method public int getHeight();
- method public final int getHyphenationFrequency();
- method public final int getJustificationMode();
- method @Nullable public final int[] getLeftIndents();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getHyphenationFrequency();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final int getJustificationMode();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getLeftIndents();
method public final int getLineAscent(int);
method public final int getLineBaseline(int);
method public final int getLineBottom(int);
method public int getLineBottom(int, boolean);
method public int getLineBounds(int, android.graphics.Rect);
- method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
method public abstract boolean getLineContainsTab(int);
method public abstract int getLineCount();
method public abstract int getLineDescent(int);
@@ -46828,13 +46836,13 @@
method public float getLineLeft(int);
method public float getLineMax(int);
method public float getLineRight(int);
- method public final float getLineSpacingAmount();
- method public final float getLineSpacingMultiplier();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingAmount();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final float getLineSpacingMultiplier();
method public abstract int getLineStart(int);
method public abstract int getLineTop(int);
method public int getLineVisibleEnd(int);
method public float getLineWidth(int);
- method @IntRange(from=1) public final int getMaxLines();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @IntRange(from=1) public final int getMaxLines();
method public int getOffsetForHorizontal(int, float);
method public int getOffsetToLeftOf(int);
method public int getOffsetToRightOf(int);
@@ -46845,19 +46853,19 @@
method public final int getParagraphRight(int);
method public float getPrimaryHorizontal(int);
method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy);
- method @Nullable public final int[] getRightIndents();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @Nullable public final int[] getRightIndents();
method public float getSecondaryHorizontal(int);
method public void getSelectionPath(int, int, android.graphics.Path);
method public final float getSpacingAdd();
method public final float getSpacingMultiplier();
- method @NonNull public final CharSequence getText();
- method @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final CharSequence getText();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
method public abstract int getTopPadding();
- method public boolean getUseBoundsForWidth();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
method @IntRange(from=0) public final int getWidth();
method public final void increaseWidthTo(int);
method public boolean isFallbackLineSpacingEnabled();
- method public final boolean isFontPaddingIncluded();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public final boolean isFontPaddingIncluded();
method public boolean isRtlCharAt(int);
method protected final boolean isSpanned();
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -46885,7 +46893,7 @@
enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE;
}
- public static final class Layout.Builder {
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public static final class Layout.Builder {
ctor public Layout.Builder(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextPaint, @IntRange(from=0) int);
method @NonNull public android.text.Layout build();
method @NonNull public android.text.Layout.Builder setAlignment(@NonNull android.text.Layout.Alignment);
@@ -46903,7 +46911,7 @@
method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
- method @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
}
public static class Layout.Directions {
@@ -59880,7 +59888,7 @@
method public final android.text.method.TransformationMethod getTransformationMethod();
method public android.graphics.Typeface getTypeface();
method public android.text.style.URLSpan[] getUrls();
- method public boolean getUseBoundsForWidth();
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public boolean getUseBoundsForWidth();
method public boolean hasSelection();
method public boolean isAllCaps();
method public boolean isCursorVisible();
@@ -60023,7 +60031,7 @@
method public final void setTransformationMethod(android.text.method.TransformationMethod);
method public void setTypeface(@Nullable android.graphics.Typeface, int);
method public void setTypeface(@Nullable android.graphics.Typeface);
- method public void setUseBoundsForWidth(boolean);
+ method @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public void setUseBoundsForWidth(boolean);
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index eba1fbe..2d070bc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3798,13 +3798,6 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
- public class PackageArchiver {
- method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- field public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
- field public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
- }
-
public class PackageInfo implements android.os.Parcelable {
field public boolean isArchived;
}
@@ -3812,6 +3805,8 @@
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
+ method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3822,6 +3817,8 @@
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
+ field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
@@ -3906,7 +3903,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
- method @NonNull public android.content.pm.PackageArchiver getPackageArchiver();
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
@@ -3932,6 +3928,7 @@
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
+ method @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fcd13b8..0255860 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -64,7 +64,6 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
-import android.content.pm.PackageArchiver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
@@ -173,7 +172,6 @@
private volatile UserManager mUserManager;
private volatile PermissionManager mPermissionManager;
private volatile PackageInstaller mInstaller;
- private volatile PackageArchiver mPackageArchiver;
private volatile ArtManager mArtManager;
private volatile DevicePolicyManager mDevicePolicyManager;
private volatile String mPermissionsControllerPackageName;
@@ -3284,18 +3282,6 @@
}
@Override
- public PackageArchiver getPackageArchiver() {
- if (mPackageArchiver == null) {
- try {
- mPackageArchiver = new PackageArchiver(mContext, mPM.getPackageArchiverService());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mPackageArchiver;
- }
-
- @Override
public boolean isPackageAvailable(String packageName) {
try {
return mPM.isPackageAvailable(packageName, getUserId());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f156878..e9bbed3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5274,7 +5274,7 @@
* Broadcast Action: Sent to the responsible installer of an archived package when unarchival
* is requested.
*
- * @see android.content.pm.PackageArchiver
+ * @see android.content.pm.PackageInstaller#requestUnarchive(String)
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/pm/IPackageArchiverService.aidl b/core/java/android/content/pm/IPackageArchiverService.aidl
deleted file mode 100644
index dc6491d..0000000
--- a/core/java/android/content/pm/IPackageArchiverService.aidl
+++ /dev/null
@@ -1,29 +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.content.pm;
-
-import android.content.IntentSender;
-import android.os.UserHandle;
-
-/** {@hide} */
-interface IPackageArchiverService {
-
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
- void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
-
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
- void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle);
-}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index ebe2aa3..edb07ce 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -24,6 +24,7 @@
import android.content.pm.VersionedPackage;
import android.content.IntentSender;
import android.os.RemoteCallback;
+import android.os.UserHandle;
import android.graphics.Bitmap;
@@ -75,4 +76,10 @@
void waitForInstallConstraints(String installerPackageName, in List<String> packageNames,
in PackageInstaller.InstallConstraints constraints, in IntentSender callback,
long timeout);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
+ void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
+ void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 916c249..556c794 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,7 +26,6 @@
import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
-import android.content.pm.IPackageArchiverService;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
import android.content.pm.IOnChecksumsReadyListener;
@@ -652,8 +651,6 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
IPackageInstaller getPackageInstaller();
- IPackageArchiverService getPackageArchiverService();
-
@EnforcePermission("DELETE_PACKAGES")
boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
@UnsupportedAppUsage
diff --git a/core/java/android/content/pm/PackageArchiver.java b/core/java/android/content/pm/PackageArchiver.java
deleted file mode 100644
index b065231..0000000
--- a/core/java/android/content/pm/PackageArchiver.java
+++ /dev/null
@@ -1,129 +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.content.pm;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.content.IntentSender;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.ParcelableException;
-import android.os.RemoteException;
-
-/**
- * {@code ArchiveManager} is used to archive apps. During the archival process, the apps APKs and
- * cache are removed from the device while the user data is kept. Through the
- * {@code requestUnarchive()} call, apps can be restored again through their responsible app store.
- *
- * <p> Archived apps are returned as displayable apps through the {@link LauncherApps} APIs and
- * will be displayed to users with UI treatment to highlight that said apps are archived. If
- * a user taps on an archived app, the app will be unarchived and the restoration process is
- * communicated.
- *
- * @hide
- */
-// TODO(b/278560219) Improve public documentation.
-@SystemApi
-public class PackageArchiver {
-
- /**
- * Extra field for the package name of a package that is requested to be unarchived. Sent as
- * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
- *
- * @hide
- */
- @SystemApi
- public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
- "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
-
- /**
- * If true, the requestor of the unarchival has specified that the app should be unarchived
- * for {@link android.os.UserHandle#ALL}.
- *
- * @hide
- */
- @SystemApi
- public static final String EXTRA_UNARCHIVE_ALL_USERS =
- "android.content.pm.extra.UNARCHIVE_ALL_USERS";
-
- private final Context mContext;
- private final IPackageArchiverService mService;
-
- /**
- * @hide
- */
- public PackageArchiver(Context context, IPackageArchiverService service) {
- mContext = context;
- mService = service;
- }
-
- /**
- * Requests to archive a package which is currently installed.
- *
- * @param statusReceiver Callback used to notify when the operation is completed.
- * @throws NameNotFoundException If {@code packageName} isn't found or not available to the
- * caller or isn't archived.
- * @hide
- */
- @RequiresPermission(anyOf = {
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.REQUEST_DELETE_PACKAGES})
- @SystemApi
- public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
- throws NameNotFoundException {
- try {
- mService.requestArchive(packageName, mContext.getPackageName(), statusReceiver,
- mContext.getUser());
- } catch (ParcelableException e) {
- e.maybeRethrow(NameNotFoundException.class);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Requests to unarchive a currently archived package.
- *
- * <p> Sends a request to unarchive an app to the responsible installer. The installer is
- * determined by {@link InstallSourceInfo#getUpdateOwnerPackageName()}, or
- * {@link InstallSourceInfo#getInstallingPackageName()} if the former value is null.
- *
- * <p> The installation will happen asynchronously and can be observed through
- * {@link android.content.Intent#ACTION_PACKAGE_ADDED}.
- *
- * @throws NameNotFoundException If {@code packageName} isn't found or not visible to the
- * caller or if the package has no installer on the device
- * anymore to unarchive it.
- * @hide
- */
- @RequiresPermission(anyOf = {
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.REQUEST_INSTALL_PACKAGES})
- @SystemApi
- public void requestUnarchive(@NonNull String packageName)
- throws NameNotFoundException {
- try {
- mService.requestUnarchive(packageName, mContext.getPackageName(), mContext.getUser());
- } catch (ParcelableException e) {
- e.maybeRethrow(NameNotFoundException.class);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index cdb8b46..1fe1923 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.IntentSender;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -492,7 +493,8 @@
/**
* Whether the package is currently in an archived state.
*
- * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
+ * <p>Packages can be archived through
+ * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
* on the device, but do keep the data directory.
* @hide
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f417480..390efd4 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -34,6 +34,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -348,6 +349,28 @@
"android.content.pm.extra.RESOLVED_BASE_PATH";
/**
+ * Extra field for the package name of a package that is requested to be unarchived. Sent as
+ * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
+ "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+
+ /**
+ * If true, the requestor of the unarchival has specified that the app should be unarchived
+ * for {@link android.os.UserHandle#ALL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public static final String EXTRA_UNARCHIVE_ALL_USERS =
+ "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
@@ -2158,6 +2181,72 @@
return new InstallInfo(result);
}
+ /**
+ * Requests to archive a package which is currently installed.
+ *
+ * <p> During the archival process, the apps APKs and cache are removed from the device while
+ * the user data is kept. Through the {@link #requestUnarchive(String)} call, apps can be
+ * restored again through their responsible installer.
+ *
+ * <p> Archived apps are returned as displayable apps through the {@link LauncherApps} APIs and
+ * will be displayed to users with UI treatment to highlight that said apps are archived. If
+ * a user taps on an archived app, the app will be unarchived and the restoration process is
+ * communicated.
+ *
+ * @param statusReceiver Callback used to notify when the operation is completed.
+ * @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
+ * available to the caller or isn't archived.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+ throws PackageManager.NameNotFoundException {
+ try {
+ mInstaller.requestArchive(packageName, mInstallerPackageName, statusReceiver,
+ new UserHandle(mUserId));
+ } catch (ParcelableException e) {
+ e.maybeRethrow(PackageManager.NameNotFoundException.class);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests to unarchive a currently archived package.
+ *
+ * <p> Sends a request to unarchive an app to the responsible installer. The installer is
+ * determined by {@link InstallSourceInfo#getUpdateOwnerPackageName()}, or
+ * {@link InstallSourceInfo#getInstallingPackageName()} if the former value is null.
+ *
+ * <p> The installation will happen asynchronously and can be observed through
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED}.
+ *
+ * @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
+ * visible to the caller or if the package has no
+ * installer on the device anymore to unarchive it.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.REQUEST_INSTALL_PACKAGES})
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public void requestUnarchive(@NonNull String packageName)
+ throws PackageManager.NameNotFoundException {
+ try {
+ mInstaller.requestUnarchive(packageName, mInstallerPackageName,
+ new UserHandle(mUserId));
+ } catch (ParcelableException e) {
+ e.maybeRethrow(PackageManager.NameNotFoundException.class);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
// (b/239722738) This class serves as a bridge between the PackageLite class, which
// is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java)
// This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9a53a2a6..cab2619 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.LongDef;
@@ -29,6 +30,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.StringRes;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -1238,8 +1240,9 @@
/**
* Flag parameter to also retrieve some information about archived packages.
- * Packages can be archived through {@link PackageArchiver} and do not have any APKs stored on
- * the device, but do keep the data directory.
+ * Packages can be archived through
+ * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
+ * on the device, but do keep the data directory.
* <p> Note: Archived apps are a subset of apps returned by {@link #MATCH_UNINSTALLED_PACKAGES}.
* <p> Note: this flag may cause less information about currently installed
* applications to be returned.
@@ -9751,7 +9754,10 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
@RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true)
+ @SuppressLint("NullableCollection")
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
@@ -9958,16 +9964,6 @@
public abstract @NonNull PackageInstaller getPackageInstaller();
/**
- * {@link PackageArchiver} can be used to archive and restore archived packages.
- *
- * @hide
- */
- @SystemApi
- public @NonNull PackageArchiver getPackageArchiver() {
- throw new UnsupportedOperationException(
- "getPackageArchiver not implemented in subclass");
- }
- /**
* Adds a {@code CrossProfileIntentFilter}. After calling this method all
* intents sent from the user with id sourceUserId can also be be resolved
* by activities in the user with id targetUserId if they match the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 41ba1dc..4b579e7 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1445,7 +1445,7 @@
verified.getPublicKeys(),
verified.getPastSigningCertificates());
} else {
- if (!Signature.areExactMatch(pkg.mSigningDetails.signatures,
+ if (!Signature.areExactArraysMatch(pkg.mSigningDetails.signatures,
verified.getSignatures())) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
@@ -6468,7 +6468,7 @@
}
}
} else {
- return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+ return Signature.areEffectiveArraysMatch(oldDetails.signatures, signatures);
}
return false;
}
@@ -6616,7 +6616,7 @@
/** Returns true if the signatures in this and other match exactly. */
public boolean signaturesMatchExactly(SigningDetails other) {
- return Signature.areExactMatch(this.signatures, other.signatures);
+ return Signature.areExactArraysMatch(this.signatures, other.signatures);
}
@Override
@@ -6668,7 +6668,7 @@
SigningDetails that = (SigningDetails) o;
if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
- if (!Signature.areExactMatch(signatures, that.signatures)) return false;
+ if (!Signature.areExactArraysMatch(signatures, that.signatures)) return false;
if (publicKeys != null) {
if (!publicKeys.equals((that.publicKeys))) {
return false;
@@ -6677,7 +6677,8 @@
return false;
}
- // can't use Signature.areExactMatch() because order matters with the past signing certs
+ // can't use Signature.areExactArraysMatch() because order matters with the past
+ // signing certs
if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
return false;
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index b049880..a69eee7 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -307,11 +307,27 @@
}
/**
- * Test if given {@link Signature} sets are exactly equal.
- *
+ * Test if given {@link SigningDetails} are exactly equal.
* @hide
*/
- public static boolean areExactMatch(Signature[] a, Signature[] b) {
+ public static boolean areExactMatch(SigningDetails ad, SigningDetails bd) {
+ return areExactArraysMatch(ad.getSignatures(), bd.getSignatures());
+ }
+
+ /**
+ * Test if given {@link SigningDetails} and {@link Signature} set are exactly equal.
+ * @hide
+ */
+ public static boolean areExactMatch(SigningDetails ad, Signature[] b) {
+ return areExactArraysMatch(ad.getSignatures(), b);
+ }
+
+
+ /**
+ * Test if given {@link Signature} sets are exactly equal.
+ * @hide
+ */
+ static boolean areExactArraysMatch(Signature[] a, Signature[] b) {
return (ArrayUtils.size(a) == ArrayUtils.size(b)) && ArrayUtils.containsAll(a, b)
&& ArrayUtils.containsAll(b, a);
}
@@ -329,7 +345,12 @@
* substantially, usually a signal of something fishy going on.
* @hide
*/
- public static boolean areEffectiveMatch(Signature[] a, Signature[] b)
+ public static boolean areEffectiveMatch(SigningDetails a, SigningDetails b)
+ throws CertificateException {
+ return areEffectiveArraysMatch(a.getSignatures(), b.getSignatures());
+ }
+
+ static boolean areEffectiveArraysMatch(Signature[] a, Signature[] b)
throws CertificateException {
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
@@ -342,7 +363,7 @@
bPrime[i] = bounce(cf, b[i]);
}
- return areExactMatch(aPrime, bPrime);
+ return areExactArraysMatch(aPrime, bPrime);
}
/**
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
index af2649f..8c21974 100644
--- a/core/java/android/content/pm/SigningDetails.java
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -656,7 +656,7 @@
}
}
} else {
- return Signature.areEffectiveMatch(oldDetails.mSignatures, mSignatures);
+ return Signature.areEffectiveMatch(oldDetails, this);
}
return false;
}
@@ -800,7 +800,7 @@
/** Returns true if the signatures in this and other match exactly. */
public boolean signaturesMatchExactly(@NonNull SigningDetails other) {
- return Signature.areExactMatch(mSignatures, other.mSignatures);
+ return Signature.areExactMatch(this, other);
}
@Override
@@ -853,7 +853,7 @@
final SigningDetails that = (SigningDetails) o;
if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false;
- if (!Signature.areExactMatch(mSignatures, that.mSignatures)) return false;
+ if (!Signature.areExactMatch(this, that)) return false;
if (mPublicKeys != null) {
if (!mPublicKeys.equals((that.mPublicKeys))) {
return false;
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
new file mode 100644
index 0000000..0c8eb02
--- /dev/null
+++ b/core/java/android/content/pm/flags.aconfig
@@ -0,0 +1,15 @@
+package: "android.content.pm"
+
+flag {
+ name: "quarantined_enabled"
+ namespace: "package_manager_service"
+ description: "Feature flag for Quarantined state"
+ bug: "269127435"
+}
+
+flag {
+ name: "archiving"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the archiving feature."
+ bug: "278553670"
+}
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 3e1c5bb..153dd9a 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -253,8 +253,8 @@
if (existingSigningDetails == SigningDetails.UNKNOWN) {
return verified;
} else {
- if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
- verified.getResult().getSignatures())) {
+ if (!Signature.areExactMatch(existingSigningDetails,
+ verified.getResult())) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f1ae9be..7967db6 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -685,7 +685,12 @@
private ImeTracker.Token mCurStatsToken;
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
+ Log.i("b/297000797", "IME#OnComputeInternalInsetsListener, start info: " + info
+ + " before onComputeInsets, tmpInsets: " + mTmpInsets,
+ new Throwable());
onComputeInsets(mTmpInsets);
+ Log.i("b/297000797", "IME#OnComputeInternalInsetsListener,"
+ + " after onComputeInsets, tmpInsets: " + mTmpInsets);
if (!mViewsCreated) {
// The IME views are not ready, keep visible insets untouched.
mTmpInsets.visibleTopInsets = 0;
@@ -705,6 +710,7 @@
}
mNavigationBarController.updateTouchableInsets(mTmpInsets, info);
+ Log.i("b/297000797", "IME#OnComputeInternalInsetsListener, end info: " + info);
if (mInputFrame != null) {
setImeExclusionRect(mTmpInsets.visibleTopInsets);
}
@@ -1463,6 +1469,15 @@
proto.write(TOUCHABLE_REGION, touchableRegion.toString());
proto.end(token);
}
+
+ @Override
+ public String toString() {
+ return "Insets{contentTopInsets=" + contentTopInsets
+ + " visibleTopInsets=" + visibleTopInsets
+ + " touchableInsets=" + touchableInsets
+ + " touchableRegion=" + touchableRegion.getBounds()
+ + "}";
+ }
}
/**
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 8be4c58..22792a5 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -31,6 +31,7 @@
import android.graphics.Region;
import android.inputmethodservice.navigationbar.NavigationBarFrame;
import android.inputmethodservice.navigationbar.NavigationBarView;
+import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -114,6 +115,8 @@
}
void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ Log.i("b/297000797", "NavigationBarController#onNavButtonFlagsChanged: " + navButtonFlags,
+ new Throwable());
mImpl.onNavButtonFlagsChanged(navButtonFlags);
}
@@ -235,6 +238,10 @@
if (ENABLE_HIDE_IME_CAPTION_BAR) {
mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
+ Log.i("b/297000797", "NavigationBarController#onApplyWindowInsetsListener:"
+ + " mNavigationBarFrame: " + mNavigationBarFrame
+ + " captionBar visible: " + insets.isVisible(captionBar())
+ + " insets: " + insets);
if (mNavigationBarFrame != null) {
boolean visible = insets.isVisible(captionBar());
mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -453,6 +460,10 @@
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ Log.i("b/297000797", "NavigationBarController#onNavButtonFlagsChanged,"
+ + " calling setImeCaptionBarInsetsHeight"
+ + " with: " + getImeCaptionBarHeight(),
+ new Throwable());
mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
.setImeCaptionBarInsetsHeight(getImeCaptionBarHeight());
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b962d76..7e71134f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10664,6 +10664,14 @@
"search_press_hold_nav_handle_enabled";
/**
+ * Whether long-pressing on the home button can trigger search.
+ *
+ * @hide
+ */
+ public static final String SEARCH_LONG_PRESS_HOME_ENABLED =
+ "search_long_press_home_enabled";
+
+ /**
* Control whether Night display is currently activated.
* @hide
*/
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 14fc585..65a1da6 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,9 +58,7 @@
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
* needed to avoid clipping in some scripts
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
boolean includePad) {
@@ -83,9 +84,7 @@
* @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerWidth} is used instead
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics,
boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -117,9 +116,7 @@
* False for keeping the first font's line height. If some glyphs
* requires larger vertical spaces, by passing true to this
* argument, the layout increase the line height to fit all glyphs.
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static @NonNull BoringLayout make(
@NonNull CharSequence source, @NonNull TextPaint paint,
@IntRange(from = 0) int outerWidth,
@@ -266,9 +263,7 @@
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
* needed to avoid clipping in some scripts
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align,
float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) {
super(source, paint, outerwidth, align, TextDirectionHeuristics.LTR, spacingMult,
@@ -302,9 +297,7 @@
* @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerWidth} is used instead
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -333,9 +326,7 @@
* False for keeping the first font's line height. If some glyphs
* requires larger vertical spaces, by passing true to this
* argument, the layout increase the line height to fit all glyphs.
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public BoringLayout(
@NonNull CharSequence source, @NonNull TextPaint paint,
@IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult,
@@ -478,9 +469,7 @@
* @param paint a paint
* @return layout metric for the given text. null if given text is unable to be handled by
* BoringLayout.
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static Metrics isBoring(CharSequence text, TextPaint paint) {
return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
}
@@ -495,9 +484,7 @@
* @return layout metric for the given text. If metrics is not null, this method fills values
* to given metrics object instead of allocating new metrics object. null if given text
* is unable to be handled by BoringLayout.
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
}
@@ -557,9 +544,7 @@
* argument, the layout increase the line height to fit all glyphs.
* @param metrics the out metrics.
* @return metrics on success. null if text cannot be rendered by BoringLayout.
- * @deprecated Use {@link android.text.Layout.Builder} instead.
*/
- @Deprecated
public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
@Nullable Metrics metrics) {
@@ -746,6 +731,7 @@
*
* @return a drawing bounding box.
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
@NonNull public RectF getDrawingBoundingBox() {
return mDrawingBounds;
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 469e166..4f4dea7 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -1010,6 +1013,7 @@
* @return bounding rectangle
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public RectF computeDrawingBoundingBox() {
float left = 0;
float right = 0;
@@ -3436,6 +3440,7 @@
*
* @see StaticLayout.Builder
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public static final class Builder {
/**
* Construct a builder class.
@@ -3776,6 +3781,7 @@
// The corresponding getter is getUseBoundsForWidth
@NonNull
@SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
@@ -3865,6 +3871,7 @@
* @see Layout.Builder
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final CharSequence getText() {
return mText;
}
@@ -3914,6 +3921,7 @@
* @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic)
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final TextDirectionHeuristic getTextDirectionHeuristic() {
return mTextDir;
}
@@ -3940,6 +3948,7 @@
* @see StaticLayout.Builder#setLineSpacing(float, float)
* @see Layout#getSpacingMultiplier()
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final float getLineSpacingMultiplier() {
return mSpacingMult;
}
@@ -3966,6 +3975,7 @@
* @see StaticLayout.Builder#setLineSpacing(float, float)
* @see Layout#getSpacingAdd()
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final float getLineSpacingAmount() {
return mSpacingAdd;
}
@@ -3977,6 +3987,7 @@
* @see Layout.Builder#setFontPaddingIncluded(boolean)
* @see StaticLayout.Builder#setIncludePad(boolean)
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final boolean isFontPaddingIncluded() {
return mIncludePad;
}
@@ -4024,6 +4035,7 @@
* @see Layout#getEllipsizedWidth()
*/
@Nullable
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final TextUtils.TruncateAt getEllipsize() {
return mEllipsize;
}
@@ -4039,6 +4051,7 @@
* @see StaticLayout.Builder#setMaxLines(int)
*/
@IntRange(from = 1)
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int getMaxLines() {
return mMaxLines;
}
@@ -4051,6 +4064,7 @@
* @see StaticLayout.Builder#setBreakStrategy(int)
*/
@BreakStrategy
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int getBreakStrategy() {
return mBreakStrategy;
}
@@ -4063,6 +4077,7 @@
* @see StaticLayout.Builder#setHyphenationFrequency(int)
*/
@HyphenationFrequency
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int getHyphenationFrequency() {
return mHyphenationFrequency;
}
@@ -4078,6 +4093,7 @@
* @see StaticLayout.Builder#setIndents(int[], int[])
*/
@Nullable
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int[] getLeftIndents() {
if (mLeftIndents == null) {
return null;
@@ -4098,6 +4114,7 @@
* @see StaticLayout.Builder#setIndents(int[], int[])
*/
@Nullable
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int[] getRightIndents() {
if (mRightIndents == null) {
return null;
@@ -4115,6 +4132,7 @@
* @see StaticLayout.Builder#setJustificationMode(int)
*/
@JustificationMode
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public final int getJustificationMode() {
return mJustificationMode;
}
@@ -4128,6 +4146,7 @@
*/
// not being final because of subclass has already published API.
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public LineBreakConfig getLineBreakConfig() {
return mLineBreakConfig;
}
@@ -4141,6 +4160,7 @@
* @see StaticLayout.Builder#setUseBoundsForWidth(boolean)
* @see DynamicLayout.Builder#setUseBoundsForWidth(boolean)
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public boolean getUseBoundsForWidth() {
return mUseBoundsForWidth;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 12527e9..0ed275c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -208,6 +208,14 @@
public static final String SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION =
"settings_remote_device_credential_validation";
+ // TODO(b/295516544): Remove this when trunk stable feature flag is available.
+ /**
+ * Flag to enable / disable the Private Space Settings. It's disabled by default.
+ * @hide
+ */
+ public static final String SETTINGS_PRIVATE_SPACE_SETTINGS =
+ "settings_private_space_settings";
+
private static final Map<String, String> DEFAULT_FLAGS;
@@ -256,6 +264,7 @@
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
// TODO: b/298454866 Replace with Trunk Stable Feature Flag
DEFAULT_FLAGS.put(SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_PRIVATE_SPACE_SETTINGS, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index d2a18dd..a6724da 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -48,6 +48,7 @@
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -428,7 +429,7 @@
// make sure all entries use the same signing certs
final Signature[] entrySigs = convertToSignatures(entryCerts);
- if (!Signature.areExactMatch(lastSigs, entrySigs)) {
+ if (!Arrays.equals(lastSigs, entrySigs)) {
return input.error(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Package " + apkPath + " has mismatched certificates at entry "
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index fb24211..9186e49 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1856,6 +1856,8 @@
return;
}
Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom);
+ Log.i("b/297000797", "InsetsController#setImeCaptionBarInsetsHeight,"
+ + " height: " + height + " frame: " + mFrame);
InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR);
if (mImeCaptionBarInsetsHeight != height
|| (source != null && !newFrame.equals(source.getFrame()))) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 0d5704e..3e435ae 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -31,6 +31,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.WindowInsets.Type.InsetsType;
@@ -196,6 +197,12 @@
* source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ if (getType() == WindowInsets.Type.ime()) {
+ Log.i("b/297000797", "InsetsSource#calculateInsets tmpFrame: " + mTmpFrame
+ + " ignoreVisibility: " + ignoreVisibility
+ + " frame: " + mFrame
+ + " relativeFrame: " + relativeFrame, new Throwable());
+ }
return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
}
@@ -203,6 +210,12 @@
* Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets.
*/
public Insets calculateVisibleInsets(Rect relativeFrame) {
+ if (getType() == WindowInsets.Type.ime()) {
+ Log.i("b/297000797", "InsetsSource#calculateVisibleInsets tmpFrame: " + mTmpFrame
+ + " frame: " + mFrame
+ + " visibleFrame: " + mVisibleFrame
+ + " relativeFrame: " + relativeFrame, new Throwable());
+ }
return calculateInsets(relativeFrame, mVisibleFrame != null ? mVisibleFrame : mFrame,
false /* ignoreVisibility */);
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 59e0932..eac7408 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -45,6 +45,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
@@ -380,6 +381,9 @@
@InternalInsetsSide @Nullable SparseIntArray idSideMap,
@Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
int index = indexOf(type);
+ if (source.getId() == InsetsSource.ID_IME) {
+ Log.i("b/297000797", "InsetsState#processSourceAsPublicType, ime insets: " + insets);
+ }
// Don't put Insets.NONE into typeInsetsMap. Otherwise, two WindowInsets can be considered
// as non-equal while they provide the same insets of each type from WindowInsets#getInsets
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index c9526fd..9e8ca20 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -315,6 +315,15 @@
}
@Override
+ public String toString() {
+ return "InternalInsetsInfo{contentInsets=" + contentInsets
+ + " visibleInsets=" + visibleInsets
+ + " touchableRegion=" + touchableRegion.getBounds()
+ + "}";
+
+ }
+
+ @Override
public int hashCode() {
int result = contentInsets.hashCode();
result = 31 * result + visibleInsets.hashCode();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f6b337c..59344b0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,12 +27,14 @@
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import android.R;
import android.annotation.CallSuper;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -238,6 +240,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
+import com.android.text.flags.Flags;
import libcore.util.EmptyArray;
@@ -523,6 +526,15 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.P)
public static final long STATICLAYOUT_FALLBACK_LINESPACING = 37756858; // buganizer id
+
+ /**
+ * This change ID enables the bounding box based layout.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long USE_BOUNDS_FOR_WIDTH = 63938206; // buganizer id
+
// System wide time for last cut, copy or text changed action.
static long sLastCutCopyOrTextChangedTime;
@@ -1621,7 +1633,11 @@
mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_NONE;
}
- mUseBoundsForWidth = false; // TODO: Make enable this by default.
+ if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
+ mUseBoundsForWidth = Flags.useBoundsForWidth();
+ } else {
+ mUseBoundsForWidth = false;
+ }
// TODO(b/179693024): Use a ChangeId instead.
mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R;
@@ -4848,6 +4864,7 @@
* width.
* @see #getUseBoundsForWidth()
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public void setUseBoundsForWidth(boolean useBoundsForWidth) {
if (mUseBoundsForWidth != useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
@@ -4865,6 +4882,7 @@
* @see #setUseBoundsForWidth(boolean)
* @return True if using bounding box for width, false if using advance for width.
*/
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public boolean getUseBoundsForWidth() {
return mUseBoundsForWidth;
}
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index 89271b5..6884e63 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -89,9 +89,10 @@
return view;
}
+ private final WeakReference<Context> mContext;
private final Resources mResources;
private final WeakReference<WindowManager> mWindowManager;
- private final WeakReference<AccessibilityManager> mAccessibilityManager;
+ private final IAccessibilityManager mAccessibilityManagerService;
private final INotificationManager mNotificationManager;
private final String mPackageName;
private final String mContextPackageName;
@@ -101,21 +102,14 @@
public ToastPresenter(Context context, IAccessibilityManager accessibilityManager,
INotificationManager notificationManager, String packageName) {
+ mContext = new WeakReference<>(context);
mResources = context.getResources();
mWindowManager = new WeakReference<>(context.getSystemService(WindowManager.class));
mNotificationManager = notificationManager;
mPackageName = packageName;
mContextPackageName = context.getPackageName();
mParams = createLayoutParams();
-
- // We obtain AccessibilityManager manually via its constructor instead of using method
- // AccessibilityManager.getInstance() for 2 reasons:
- // 1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
- // 2. getInstance() caches the instance for the process even if we pass a different
- // context to it. This is problematic for multi-user because callers can pass a context
- // created via Context.createContextAsUser().
- mAccessibilityManager = new WeakReference<>(
- new AccessibilityManager(context, accessibilityManager, context.getUserId()));
+ mAccessibilityManagerService = accessibilityManager;
}
public String getPackageName() {
@@ -306,11 +300,20 @@
* enabled.
*/
public void trySendAccessibilityEvent(View view, String packageName) {
- final AccessibilityManager accessibilityManager = mAccessibilityManager.get();
- if (accessibilityManager == null) {
+ final Context context = mContext.get();
+ if (context == null) {
return;
}
+ // We obtain AccessibilityManager manually via its constructor instead of using method
+ // AccessibilityManager.getInstance() for 2 reasons:
+ // 1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
+ // 2. getInstance() caches the instance for the process even if we pass a different
+ // context to it. This is problematic for multi-user because callers can pass a context
+ // created via Context.createContextAsUser().
+ final AccessibilityManager accessibilityManager = new AccessibilityManager(context,
+ mAccessibilityManagerService, context.getUserId());
+
if (!accessibilityManager.isEnabled()) {
accessibilityManager.removeClient();
return;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 3e16df4d..9d66174 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1123,6 +1123,7 @@
final Insets systemInsets = clearsCompatInsets
? Insets.NONE
: Insets.min(insets.getInsets(compatInsetsTypes), stableBarInsets);
+ Log.i("b/297000797", "DecorView#updateColorViews, systemInsets: " + systemInsets);
mLastTopInset = systemInsets.top;
mLastBottomInset = systemInsets.bottom;
mLastRightInset = systemInsets.right;
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 5bc48c5..c9d9926 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -19,6 +19,9 @@
import android.annotation.NonNull;
import android.os.FileUtils;
import android.util.Log;
+import android.util.Pair;
+
+import libcore.io.IoUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -28,12 +31,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Comparator;
import java.util.Objects;
+import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
-import libcore.io.IoUtils;
-
/**
* Utility that rotates files over time, similar to {@code logrotate}. There is
* a single "active" file, which is periodically rotated into historical files,
@@ -302,17 +305,24 @@
public void readMatching(Reader reader, long matchStartMillis, long matchEndMillis)
throws IOException {
final FileInfo info = new FileInfo(mPrefix);
+ final TreeSet<Pair<Long, String>> readSet = new TreeSet<>(
+ Comparator.comparingLong(o -> o.first));
for (String name : mBasePath.list()) {
if (!info.parse(name)) continue;
- // read file when it overlaps
+ // Add file to set when it overlaps.
if (info.startMillis <= matchEndMillis && matchStartMillis <= info.endMillis) {
- if (LOGD) Log.d(TAG, "reading matching " + name);
-
- final File file = new File(mBasePath, name);
- readFile(file, reader);
+ readSet.add(new Pair(info.startMillis, name));
}
}
+
+ // Read files in ascending order of start timestamp.
+ for (Pair<Long, String> pair : readSet) {
+ final String name = pair.second;
+ if (LOGD) Log.d(TAG, "reading matching " + name);
+ final File file = new File(mBasePath, name);
+ readFile(file, reader);
+ }
}
/**
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c368fa8..56066b2 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1806,15 +1806,10 @@
if (!is_system_server && getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
- if (rc == -ESRCH) {
- // If process is dead, treat this as a non-fatal error
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
- } else {
- fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
- "CONFIG_CGROUP_CPUACCT?")
- : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
- /* pid= */ 0, strerror(-rc)));
- }
+ fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
+ "CONFIG_CGROUP_CPUACCT?")
+ : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
+ /* pid= */ 0, strerror(-rc)));
}
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ad88092..6c93680 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -139,6 +139,7 @@
optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto search_press_hold_nav_handle_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto search_long_press_home_enabled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index fb0a435..4dd7b40 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -33,28 +33,44 @@
/** Cert B with valid syntax */
private static final Signature B = new Signature("308204a830820390a003020102020900a1573d0f45bea193300d06092a864886f70d0101050500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d301e170d3131303931393138343232355a170d3339303230343138343232355a308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d30820120300d06092a864886f70d01010105000382010d00308201080282010100de1b51336afc909d8bcca5920fcdc8940578ec5c253898930e985481cfdea75ba6fc54b1f7bb492a03d98db471ab4200103a8314e60ee25fef6c8b83bc1b2b45b084874cffef148fa2001bb25c672b6beba50b7ac026b546da762ea223829a22b80ef286131f059d2c9b4ca71d54e515a8a3fd6bf5f12a2493dfc2619b337b032a7cf8bbd34b833f2b93aeab3d325549a93272093943bb59dfc0197ae4861ff514e019b73f5cf10023ad1a032adb4b9bbaeb4debecb4941d6a02381f1165e1ac884c1fca9525c5854dce2ad8ec839b8ce78442c16367efc07778a337d3ca2cdf9792ac722b95d67c345f1c00976ec372f02bfcbef0262cc512a6845e71cfea0d020103a381fc3081f9301d0603551d0e0416041478a0fc4517fb70ff52210df33c8d32290a44b2bb3081c90603551d230481c13081be801478a0fc4517fb70ff52210df33c8d32290a44b2bba1819aa48197308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d820900a1573d0f45bea193300c0603551d13040530030101ff300d06092a864886f70d01010505000382010100977302dfbf668d7c61841c9c78d2563bcda1b199e95e6275a799939981416909722713531157f3cdcfea94eea7bb79ca3ca972bd8058a36ad1919291df42d7190678d4ea47a4b9552c9dfb260e6d0d9129b44615cd641c1080580e8a990dd768c6ab500c3b964e185874e4105109d94c5bd8c405deb3cf0f7960a563bfab58169a956372167a7e2674a04c4f80015d8f7869a7a4139aecbbdca2abc294144ee01e4109f0e47a518363cf6e9bf41f7560e94bdd4a5d085234796b05c7a1389adfd489feec2a107955129d7991daa49afb3d327dc0dc4fe959789372b093a89c8dbfa41554f771c18015a6cb242a17e04d19d55d3b4664eae12caf2a11cd2b836e");
+ private boolean areExactMatch(Signature[] a, Signature[] b) throws Exception {
+ SigningDetails ad1 = new SigningDetails(a,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ SigningDetails bd1 = new SigningDetails(b,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ return Signature.areExactMatch(ad1, bd1);
+ }
+
public void testExactlyEqual() throws Exception {
- assertTrue(Signature.areExactMatch(asArray(A), asArray(A)));
- assertTrue(Signature.areExactMatch(asArray(M), asArray(M)));
+ assertTrue(areExactMatch(asArray(A), asArray(A)));
+ assertTrue(areExactMatch(asArray(M), asArray(M)));
- assertFalse(Signature.areExactMatch(asArray(A), asArray(B)));
- assertFalse(Signature.areExactMatch(asArray(A), asArray(M)));
- assertFalse(Signature.areExactMatch(asArray(M), asArray(A)));
+ assertFalse(areExactMatch(asArray(A), asArray(B)));
+ assertFalse(areExactMatch(asArray(A), asArray(M)));
+ assertFalse(areExactMatch(asArray(M), asArray(A)));
- assertTrue(Signature.areExactMatch(asArray(A, M), asArray(M, A)));
+ assertTrue(areExactMatch(asArray(A, M), asArray(M, A)));
+ }
+
+ private boolean areEffectiveMatch(Signature[] a, Signature[] b) throws Exception {
+ SigningDetails ad1 = new SigningDetails(a,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ SigningDetails bd1 = new SigningDetails(b,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ return Signature.areEffectiveMatch(ad1, bd1);
}
public void testEffectiveMatch() throws Exception {
- assertTrue(Signature.areEffectiveMatch(asArray(A), asArray(A)));
- assertTrue(Signature.areEffectiveMatch(asArray(M), asArray(M)));
+ assertTrue(areEffectiveMatch(asArray(A), asArray(A)));
+ assertTrue(areEffectiveMatch(asArray(M), asArray(M)));
- assertFalse(Signature.areEffectiveMatch(asArray(A), asArray(B)));
- assertTrue(Signature.areEffectiveMatch(asArray(A), asArray(M)));
- assertTrue(Signature.areEffectiveMatch(asArray(M), asArray(A)));
+ assertFalse(areEffectiveMatch(asArray(A), asArray(B)));
+ assertTrue(areEffectiveMatch(asArray(A), asArray(M)));
+ assertTrue(areEffectiveMatch(asArray(M), asArray(A)));
- assertTrue(Signature.areEffectiveMatch(asArray(A, M), asArray(M, A)));
- assertTrue(Signature.areEffectiveMatch(asArray(A, B), asArray(M, B)));
- assertFalse(Signature.areEffectiveMatch(asArray(A, M), asArray(A, B)));
+ assertTrue(areEffectiveMatch(asArray(A, M), asArray(M, A)));
+ assertTrue(areEffectiveMatch(asArray(A, B), asArray(M, B)));
+ assertFalse(areEffectiveMatch(asArray(A, M), asArray(A, B)));
}
public void testHashCode_doesNotIncludeFlags() throws Exception {
diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
index 95272132..73e47e16 100644
--- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
@@ -366,6 +366,16 @@
assertReadAll(rotate, "bar");
}
+ public void testReadSorted() throws Exception {
+ write("rotator.1024-2048", "2");
+ write("rotator.2048-4096", "3");
+ write("rotator.512-1024", "1");
+
+ final FileRotator rotate = new FileRotator(
+ mBasePath, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS);
+ assertReadAll(rotate, "1", "2", "3");
+ }
+
public void testFileSystemInaccessible() throws Exception {
File inaccessibleDir = null;
String dirPath = getContext().getFilesDir() + File.separator + "inaccessible";
@@ -422,16 +432,7 @@
}
public void assertRead(String... expected) {
- assertEquals(expected.length, mActual.size());
-
- final ArrayList<String> actualCopy = new ArrayList<String>(mActual);
- for (String value : expected) {
- if (!actualCopy.remove(value)) {
- final String expectedString = Arrays.toString(expected);
- final String actualString = Arrays.toString(mActual.toArray());
- fail("expected: " + expectedString + " but was: " + actualString);
- }
- }
+ assertEquals(Arrays.asList(expected), mActual);
}
}
}
diff --git a/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml
new file mode 100644
index 0000000..8ef3307
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M4,18H20V6H4V18ZM22,18C22,19.1 21.1,20 20,20H4C2.9,20 2,19.1 2,18V6C2,4.9 2.9,4 4,4H20C21.1,4 22,4.9 22,6V18ZM13,8H18V14H13V8Z"
+ android:fillColor="#455A64"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
new file mode 100644
index 0000000..b489a5c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -0,0 +1,56 @@
+<?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
+ -->
+<com.android.wm.shell.common.bubbles.BubblePopupView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/bubble_popup_margin_horizontal"
+ android:layout_marginBottom="120dp"
+ android:elevation="@dimen/bubble_manage_menu_elevation"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?android:attr/colorAccent"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_floating_landscape"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/bubble_bar_education_stack_title"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAlignment="center"
+ android:text="@string/bubble_bar_education_stack_text"/>
+
+</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 00c63d7..eabe3a4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -163,6 +163,10 @@
<!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
<string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string>
+ <!-- Title text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
+ <string name="bubble_bar_education_stack_title">Chat using bubbles</string>
+ <!-- Descriptive text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] -->
+ <string name="bubble_bar_education_stack_text">New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them.</string>
<!-- Title text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]-->
<string name="bubble_bar_education_manage_title">Control bubbles anytime</string>
<!-- Descriptive text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index dfdc79e..f259902 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -56,6 +56,7 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Binder;
@@ -1063,6 +1064,15 @@
}
}
+ /**
+ * Show bubble bar user education relative to the reference position.
+ * @param position the reference position in Screen coordinates.
+ */
+ public void showUserEducation(Point position) {
+ if (mLayerView == null) return;
+ mLayerView.showUserEducation(position);
+ }
+
@VisibleForTesting
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
@@ -1115,6 +1125,16 @@
}
/**
+ * Expands the stack if the selected bubble is present. This is currently used when user
+ * education view is clicked to expand the selected bubble.
+ */
+ public void expandStackWithSelectedBubble() {
+ if (mBubbleData.getSelectedBubble() != null) {
+ mBubbleData.setExpanded(true);
+ }
+ }
+
+ /**
* Expands and selects the provided bubble as long as it already exists in the stack or the
* overflow. This is currently used when opening a bubble via clicking on a conversation widget.
*/
@@ -1730,7 +1750,8 @@
+ " expandedChanged=" + update.expandedChanged
+ " selectionChanged=" + update.selectionChanged
+ " suppressed=" + (update.suppressedBubble != null)
- + " unsuppressed=" + (update.unsuppressedBubble != null));
+ + " unsuppressed=" + (update.unsuppressedBubble != null)
+ + " shouldShowEducation=" + update.shouldShowEducation);
}
ensureBubbleViewsAndWindowCreated();
@@ -2155,6 +2176,12 @@
public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
mMainExecutor.execute(() -> mController.onBubbleDrag(bubbleKey, isBeingDragged));
}
+
+ @Override
+ public void showUserEducation(int positionX, int positionY) {
+ mMainExecutor.execute(() ->
+ mController.showUserEducation(new Point(positionX, positionY)));
+ }
}
private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index c6f74af..595a4af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -77,6 +77,7 @@
boolean orderChanged;
boolean suppressedSummaryChanged;
boolean expanded;
+ boolean shouldShowEducation;
@Nullable BubbleViewProvider selectedBubble;
@Nullable Bubble addedBubble;
@Nullable Bubble updatedBubble;
@@ -126,6 +127,7 @@
bubbleBarUpdate.expandedChanged = expandedChanged;
bubbleBarUpdate.expanded = expanded;
+ bubbleBarUpdate.shouldShowEducation = shouldShowEducation;
if (selectionChanged) {
bubbleBarUpdate.selectedBubbleKey = selectedBubble != null
? selectedBubble.getKey()
@@ -165,6 +167,7 @@
*/
BubbleBarUpdate getInitialState() {
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
+ bubbleBarUpdate.shouldShowEducation = shouldShowEducation;
for (int i = 0; i < bubbles.size(); i++) {
bubbleBarUpdate.currentBubbleList.add(bubbles.get(i).asBubbleBarBubble());
}
@@ -187,6 +190,7 @@
private final Context mContext;
private final BubblePositioner mPositioner;
+ private final BubbleEducationController mEducationController;
private final Executor mMainExecutor;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
@@ -233,10 +237,11 @@
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner,
- Executor mainExecutor) {
+ BubbleEducationController educationController, Executor mainExecutor) {
mContext = context;
mLogger = bubbleLogger;
mPositioner = positioner;
+ mEducationController = educationController;
mMainExecutor = mainExecutor;
mOverflow = new BubbleOverflow(context, positioner);
mBubbles = new ArrayList<>();
@@ -447,6 +452,7 @@
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
setSelectedBubbleInternal(bubble);
+
if (!mExpanded) {
setExpandedInternal(true);
}
@@ -877,6 +883,9 @@
private void dispatchPendingChanges() {
if (mListener != null && mStateChange.anythingChanged()) {
+ mStateChange.shouldShowEducation = mSelectedBubble != null
+ && mEducationController.shouldShowStackEducation(mSelectedBubble)
+ && !mExpanded;
mListener.applyUpdate(mStateChange);
}
mStateChange = new Update(mBubbles, mOverflowBubbles);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 1c0e052..f56b171 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -44,7 +44,7 @@
static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false;
static final boolean DEBUG_EXPERIMENTS = true;
static final boolean DEBUG_OVERFLOW = false;
- static final boolean DEBUG_USER_EDUCATION = false;
+ public static final boolean DEBUG_USER_EDUCATION = false;
static final boolean DEBUG_POSITIONER = false;
public static final boolean DEBUG_COLLAPSE_ANIMATOR = false;
public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 4dda068..5776ad1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -39,4 +39,6 @@
oneway void onBubbleDrag(in String key, in boolean isBeingDragged) = 7;
+ oneway void showUserEducation(in int positionX, in int positionY) = 8;
+
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 8f11253..e788341 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
@@ -36,6 +37,8 @@
import java.util.function.Consumer;
+import kotlin.Unit;
+
/**
* Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window
* manager to display bubbles. However, it is only used when bubbles are being displayed in
@@ -111,7 +114,7 @@
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
if (mExpandedView != null) {
- mEducationViewController.hideManageEducation(/* animated = */ false);
+ mEducationViewController.hideEducation(/* animated = */ false);
removeView(mExpandedView);
mExpandedView = null;
}
@@ -171,7 +174,9 @@
mExpandedView.setListener(new BubbleBarExpandedView.Listener() {
@Override
public void onTaskCreated() {
- mEducationViewController.maybeShowManageEducation(b, mExpandedView);
+ if (mEducationViewController != null && mExpandedView != null) {
+ mEducationViewController.maybeShowManageEducation(b, mExpandedView);
+ }
}
@Override
@@ -190,6 +195,10 @@
addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
}
+ if (mEducationViewController.isEducationVisible()) {
+ mEducationViewController.hideEducation(/* animated = */ true);
+ }
+
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
mAnimationHelper.animateExpansion(mExpandedBubble, () -> {
@@ -210,7 +219,7 @@
public void collapse() {
mIsExpanded = false;
final BubbleBarExpandedView viewToRemove = mExpandedView;
- mEducationViewController.hideManageEducation(/* animated = */ true);
+ mEducationViewController.hideEducation(/* animated = */ true);
mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
mBubbleController.getSysuiProxy().onStackExpandChanged(false);
mExpandedView = null;
@@ -218,6 +227,21 @@
showScrim(false);
}
+ /**
+ * Show bubble bar user education relative to the reference position.
+ * @param position the reference position in Screen coordinates.
+ */
+ public void showUserEducation(Point position) {
+ mEducationViewController.showStackEducation(position, /* root = */ this, () -> {
+ // When the user education is clicked hide it and expand the selected bubble
+ mEducationViewController.hideEducation(/* animated = */ true, () -> {
+ mBubbleController.expandStackWithSelectedBubble();
+ return Unit.INSTANCE;
+ });
+ return Unit.INSTANCE;
+ });
+ }
+
/** Sets the function to call to un-bubble the given conversation. */
public void setUnBubbleConversationCallback(
@Nullable Consumer<String> unBubbleConversationCallback) {
@@ -226,8 +250,8 @@
/** Hides the current modal education/menu view, expanded view or collapses the bubble stack */
private void hideMenuOrCollapse() {
- if (mEducationViewController.isManageEducationVisible()) {
- mEducationViewController.hideManageEducation(/* animated = */ true);
+ if (mEducationViewController.isEducationVisible()) {
+ mEducationViewController.hideEducation(/* animated = */ true);
} else if (isExpanded() && mExpandedView != null) {
mExpandedView.hideMenuOrCollapse();
} else {
@@ -275,7 +299,7 @@
*/
private void getTouchableRegion(Region outRegion) {
mTempRect.setEmpty();
- if (mIsExpanded) {
+ if (mIsExpanded || mEducationViewController.isEducationVisible()) {
getBoundsOnScreen(mTempRect);
outRegion.op(mTempRect, Region.Op.UNION);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7b39c6f..ee552ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -15,23 +15,34 @@
*/
package com.android.wm.shell.bubbles.bar
+import android.annotation.LayoutRes
import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.Log
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.core.view.doOnLayout
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
+import com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.common.bubbles.BubblePopupDrawable
import com.android.wm.shell.common.bubbles.BubblePopupView
+import kotlin.math.roundToInt
/** Manages bubble education presentation and animation */
class BubbleEducationViewController(private val context: Context, private val listener: Listener) {
interface Listener {
- fun onManageEducationVisibilityChanged(isVisible: Boolean)
+ fun onEducationVisibilityChanged(isVisible: Boolean)
}
private var rootView: ViewGroup? = null
@@ -45,61 +56,112 @@
)
}
+ private val scrimView by lazy {
+ View(context).apply {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ setOnClickListener { hideEducation(animated = true) }
+ }
+ }
+
private val controller by lazy { BubbleEducationController(context) }
/** Whether the education view is visible or being animated */
- val isManageEducationVisible: Boolean
+ val isEducationVisible: Boolean
get() = educationView != null && rootView != null
/**
+ * Hide the current education view if visible
+ *
+ * @param animated whether should hide with animation
+ */
+ @JvmOverloads
+ fun hideEducation(animated: Boolean, endActions: () -> Unit = {}) {
+ log { "hideEducation animated: $animated" }
+
+ if (animated) {
+ animateTransition(show = false) {
+ cleanUp()
+ endActions()
+ listener.onEducationVisibilityChanged(isVisible = false)
+ }
+ } else {
+ cleanUp()
+ endActions()
+ listener.onEducationVisibilityChanged(isVisible = false)
+ }
+ }
+
+ /**
+ * Show bubble bar stack user education.
+ *
+ * @param position the reference position for the user education in Screen coordinates.
+ * @param root the view to show user education in.
+ * @param educationClickHandler the on click handler for the user education view
+ */
+ fun showStackEducation(position: Point, root: ViewGroup, educationClickHandler: () -> Unit) {
+ hideEducation(animated = false)
+ log { "showStackEducation at: $position" }
+
+ educationView =
+ createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+ setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
+ setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+ updateEducationPosition(view = this, position, root)
+ val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
+ doOnLayout {
+ it.pivotX = it.width - arrowToEdgeOffset
+ it.pivotY = it.height.toFloat()
+ }
+ setOnClickListener { educationClickHandler() }
+ }
+
+ rootView = root
+ animator = createAnimator()
+
+ root.addView(scrimView)
+ root.addView(educationView)
+ animateTransition(show = true) {
+ controller.hasSeenStackEducation = true
+ listener.onEducationVisibilityChanged(isVisible = true)
+ }
+ }
+
+ /**
* Show manage bubble education if hasn't been shown before
*
* @param bubble the bubble used for the manage education check
* @param root the view to show manage education in
*/
fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) {
+ log { "maybeShowManageEducation bubble: $bubble" }
if (!controller.shouldShowManageEducation(bubble)) return
showManageEducation(root)
}
/**
- * Hide the manage education view if visible
- *
- * @param animated whether should hide with animation
- */
- fun hideManageEducation(animated: Boolean) {
- rootView?.let {
- fun cleanUp() {
- it.removeView(educationView)
- rootView = null
- listener.onManageEducationVisibilityChanged(isVisible = false)
- }
-
- if (animated) {
- animateTransition(show = false, ::cleanUp)
- } else {
- cleanUp()
- }
- }
- }
-
- /**
* Show manage education with animation
*
* @param root the view to show manage education in
*/
private fun showManageEducation(root: ViewGroup) {
- hideManageEducation(animated = false)
- if (educationView == null) {
- val eduView = createEducationView(root)
- educationView = eduView
- animator = createAnimation(eduView)
- }
- root.addView(educationView)
+ hideEducation(animated = false)
+ log { "showManageEducation" }
+
+ educationView =
+ createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+ pivotY = 0f
+ doOnLayout { it.pivotX = it.width / 2f }
+ setOnClickListener { hideEducation(animated = true) }
+ }
+
rootView = root
+ animator = createAnimator()
+
+ root.addView(scrimView)
+ root.addView(educationView)
animateTransition(show = true) {
controller.hasSeenManageEducation = true
- listener.onManageEducationVisibilityChanged(isVisible = true)
+ listener.onEducationVisibilityChanged(isVisible = true)
}
}
@@ -110,39 +172,75 @@
* @param endActions a closure to be called when the animation completes
*/
private fun animateTransition(show: Boolean, endActions: () -> Unit) {
- animator?.let { animator ->
- animator
- .spring(DynamicAnimation.ALPHA, if (show) 1f else 0f)
- .spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN)
- .spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN)
- .withEndActions(endActions)
- .start()
- } ?: endActions()
+ animator
+ ?.spring(DynamicAnimation.ALPHA, if (show) 1f else 0f)
+ ?.spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN)
+ ?.spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN)
+ ?.withEndActions(endActions)
+ ?.start()
+ ?: endActions()
}
- private fun createEducationView(root: ViewGroup): BubblePopupView {
- val view =
- LayoutInflater.from(context).inflate(R.layout.bubble_bar_manage_education, root, false)
- as BubblePopupView
+ /** Remove education view from the root and clean up all relative properties */
+ private fun cleanUp() {
+ log { "cleanUp" }
+ rootView?.removeView(educationView)
+ rootView?.removeView(scrimView)
+ educationView = null
+ rootView = null
+ animator = null
+ }
- return view.apply {
- setup()
- alpha = 0f
- pivotY = 0f
- scaleX = EDU_SCALE_HIDDEN
- scaleY = EDU_SCALE_HIDDEN
- doOnLayout { it.pivotX = it.width / 2f }
- setOnClickListener { hideManageEducation(animated = true) }
+ /**
+ * Create education view by inflating layout provided.
+ *
+ * @param layout layout resource id to inflate. The root view should be [BubblePopupView]
+ * @param root view group to use as root for inflation, is not attached to root
+ */
+ private fun createEducationView(@LayoutRes layout: Int, root: ViewGroup): BubblePopupView {
+ val view = LayoutInflater.from(context).inflate(layout, root, false) as BubblePopupView
+ view.setup()
+ view.alpha = 0f
+ view.scaleX = EDU_SCALE_HIDDEN
+ view.scaleY = EDU_SCALE_HIDDEN
+ return view
+ }
+
+ /** Create animator for the user education transitions */
+ private fun createAnimator(): PhysicsAnimator<BubblePopupView>? {
+ return educationView?.let {
+ PhysicsAnimator.getInstance(it).apply { setDefaultSpringConfig(springConfig) }
}
}
- private fun createAnimation(view: BubblePopupView): PhysicsAnimator<BubblePopupView> {
- val animator = PhysicsAnimator.getInstance(view)
- animator.setDefaultSpringConfig(springConfig)
- return animator
+ /**
+ * Update user education view position relative to the reference position
+ *
+ * @param view the user education view to layout
+ * @param position the reference position in Screen coordinates
+ * @param root the root view to use for the layout
+ */
+ private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
+ val rootBounds = Rect()
+ // Get root bounds on screen as position is in screen coordinates
+ root.getBoundsOnScreen(rootBounds)
+ // Get the offset to the arrow from the edge of the education view
+ val arrowToEdgeOffset =
+ view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
+ ?: 0
+ // Calculate education view margins
+ val params = view.layoutParams as FrameLayout.LayoutParams
+ params.bottomMargin = rootBounds.bottom - position.y
+ params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ view.layoutParams = params
+ }
+
+ private fun log(msg: () -> String) {
+ if (DEBUG_USER_EDUCATION) Log.d(TAG, msg())
}
companion object {
+ private val TAG = if (TAG_WITH_CLASS_NAME) "BubbleEducationViewController" else TAG_BUBBLES
private const val EDU_SCALE_HIDDEN = 0.5f
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
index 8142347..fc627a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
@@ -35,6 +35,7 @@
public boolean expandedChanged;
public boolean expanded;
+ public boolean shouldShowEducation;
@Nullable
public String selectedBubbleKey;
@Nullable
@@ -61,6 +62,7 @@
public BubbleBarUpdate(Parcel parcel) {
expandedChanged = parcel.readBoolean();
expanded = parcel.readBoolean();
+ shouldShowEducation = parcel.readBoolean();
selectedBubbleKey = parcel.readString();
addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
BubbleInfo.class);
@@ -95,6 +97,7 @@
return "BubbleBarUpdate{ expandedChanged=" + expandedChanged
+ " expanded=" + expanded
+ " selectedBubbleKey=" + selectedBubbleKey
+ + " shouldShowEducation=" + shouldShowEducation
+ " addedBubble=" + addedBubble
+ " updatedBubble=" + updatedBubble
+ " suppressedBubbleKey=" + suppressedBubbleKey
@@ -114,6 +117,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeBoolean(expandedChanged);
parcel.writeBoolean(expanded);
+ parcel.writeBoolean(shouldShowEducation);
parcel.writeString(selectedBubbleKey);
parcel.writeParcelable(addedBubble, flags);
parcel.writeParcelable(updatedBubble, flags);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
index 1fd22d0a..887af17 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -31,7 +31,7 @@
import kotlin.properties.Delegates
/** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */
-class BubblePopupDrawable(private val config: Config) : Drawable() {
+class BubblePopupDrawable(val config: Config) : Drawable() {
/** The direction of the arrow in the popup drawable */
enum class ArrowDirection {
UP,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
index f8a4946..444fbf7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
@@ -29,7 +29,8 @@
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
- private var popupDrawable: BubblePopupDrawable? = null
+ var popupDrawable: BubblePopupDrawable? = null
+ private set
/**
* Sets up the popup drawable with the config provided. Required to remove dependency on local
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 1901e0b..a5000fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -19,6 +19,14 @@
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import android.content.Context;
import android.content.res.Configuration;
@@ -263,7 +271,7 @@
private SnapTarget snap(int position, boolean hardDismiss) {
if (shouldApplyFreeSnapMode(position)) {
- return new SnapTarget(position, position, SnapTarget.FLAG_NONE);
+ return new SnapTarget(position, position, SNAP_TO_NONE);
}
int minIndex = -1;
float minDistance = Float.MAX_VALUE;
@@ -291,8 +299,7 @@
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
}
- mTargets.add(new SnapTarget(startPos, startPos, SnapTarget.FLAG_DISMISS_START,
- 0.35f));
+ mTargets.add(new SnapTarget(startPos, startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
addRatio16_9Targets(isHorizontalDivision, dividerMax);
@@ -307,15 +314,15 @@
addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
- mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
+ mTargets.add(new SnapTarget(dividerMax, dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
}
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
int bottomPosition, int dividerMax) {
- maybeAddTarget(topPosition, topPosition - getStartInset());
+ maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_30_70);
addMiddleTarget(isHorizontalDivision);
maybeAddTarget(bottomPosition,
- dividerMax - getEndInset() - (bottomPosition + mDividerSize));
+ dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_70_30);
}
private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
@@ -349,16 +356,16 @@
* Adds a target at {@param position} but only if the area with size of {@param smallerSize}
* meets the minimal size requirement.
*/
- private void maybeAddTarget(int position, int smallerSize) {
+ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapTo) {
if (smallerSize >= mMinimalSizeResizableTask) {
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, snapTo));
}
}
private void addMiddleTarget(boolean isHorizontalDivision) {
int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, SNAP_TO_50_50));
}
private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
@@ -372,7 +379,7 @@
position = mDisplayWidth - position - mInsets.right - mDividerSize;
}
}
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, SNAP_TO_MINIMIZE));
}
public SnapTarget getMiddleTarget() {
@@ -435,14 +442,6 @@
* Represents a snap target for the divider.
*/
public static class SnapTarget {
- public static final int FLAG_NONE = 0;
-
- /** If the divider reaches this value, the left/top task should be dismissed. */
- public static final int FLAG_DISMISS_START = 1;
-
- /** If the divider reaches this value, the right/bottom task should be dismissed */
- public static final int FLAG_DISMISS_END = 2;
-
/** Position of this snap target. The right/bottom edge of the top/left task snaps here. */
public final int position;
@@ -452,7 +451,10 @@
*/
public final int taskPosition;
- public final int flag;
+ /**
+ * An int describing the placement of the divider in this snap target.
+ */
+ public final @SnapPosition int snapTo;
public boolean isMiddleTarget;
@@ -462,14 +464,15 @@
*/
private final float distanceMultiplier;
- public SnapTarget(int position, int taskPosition, int flag) {
- this(position, taskPosition, flag, 1f);
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo) {
+ this(position, taskPosition, snapTo, 1f);
}
- public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) {
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapTo,
+ float distanceMultiplier) {
this.position = position;
this.taskPosition = taskPosition;
- this.flag = flag;
+ this.snapTo = snapTo;
this.distanceMultiplier = distanceMultiplier;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 755dba0..4af03fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -23,13 +23,12 @@
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
-
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
-import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
-import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
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;
@@ -511,13 +510,13 @@
* target indicates dismissing split.
*/
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
- switch (snapTarget.flag) {
- case FLAG_DISMISS_START:
+ switch (snapTarget.snapTo) {
+ case SNAP_TO_START_AND_DISMISS:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
break;
- case FLAG_DISMISS_END:
+ case SNAP_TO_END_AND_DISMISS:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index be1b9b1..ff38b7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -57,6 +57,38 @@
public @interface SplitPosition {
}
+ /** The divider doesn't snap to any target and is freely placeable. */
+ public static final int SNAP_TO_NONE = 0;
+
+ /** A snap target positioned near the screen edge for a minimized task */
+ public static final int SNAP_TO_MINIMIZE = 1;
+
+ /** If the divider reaches this value, the left/top task should be dismissed. */
+ public static final int SNAP_TO_START_AND_DISMISS = 2;
+
+ /** A snap target in the first half of the screen, where the split is roughly 30-70. */
+ public static final int SNAP_TO_30_70 = 3;
+
+ /** The 50-50 snap target */
+ public static final int SNAP_TO_50_50 = 4;
+
+ /** A snap target in the latter half of the screen, where the split is roughly 70-30. */
+ public static final int SNAP_TO_70_30 = 5;
+
+ /** If the divider reaches this value, the right/bottom task should be dismissed. */
+ public static final int SNAP_TO_END_AND_DISMISS = 6;
+
+ @IntDef(prefix = { "SNAP_TO_" }, value = {
+ SNAP_TO_NONE,
+ SNAP_TO_MINIMIZE,
+ SNAP_TO_START_AND_DISMISS,
+ SNAP_TO_30_70,
+ SNAP_TO_50_50,
+ SNAP_TO_70_30,
+ SNAP_TO_END_AND_DISMISS
+ })
+ public @interface SnapPosition {}
+
public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
public static final int[] CONTROLLED_WINDOWING_MODES =
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e9f3e1a..fd23d14 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -35,6 +35,7 @@
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleEducationController;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.properties.ProdBubbleProperties;
@@ -134,11 +135,18 @@
@WMSingleton
@Provides
+ static BubbleEducationController provideBubbleEducationProvider(Context context) {
+ return new BubbleEducationController(context);
+ }
+
+ @WMSingleton
+ @Provides
static BubbleData provideBubbleData(Context context,
BubbleLogger logger,
BubblePositioner positioner,
+ BubbleEducationController educationController,
@ShellMainThread ShellExecutor mainExecutor) {
- return new BubbleData(context, logger, positioner, mainExecutor);
+ return new BubbleData(context, logger, positioner, educationController, mainExecutor);
}
// Note: Handler needed for LauncherApps.register
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b918c83..40c519e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.desktopmode
-import android.R
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
@@ -27,7 +26,6 @@
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
-import android.content.res.TypedArray
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -44,6 +42,7 @@
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
@@ -696,10 +695,7 @@
finishTransaction: SurfaceControl.Transaction
) {
// Add rounded corners to freeform windows
- val ta: TypedArray = context.obtainStyledAttributes(
- intArrayOf(R.attr.dialogCornerRadius))
- val cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
- ta.recycle()
+ val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
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 87ceaa4..00f6a1c 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
@@ -19,10 +19,10 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -239,9 +239,14 @@
@Override
public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
- if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible()
- || DesktopModeStatus.isEnabled())) {
- return this;
+ if (mRecentsHandler != null) {
+ if (mSplitHandler.isSplitScreenVisible()) {
+ return this;
+ } else if (mDesktopTasksController != null
+ // Check on the default display. Recents/gesture nav is only available there
+ && mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) {
+ return this;
+ }
}
return null;
}
@@ -662,7 +667,6 @@
if (!consumed) {
return false;
}
- //Sync desktop mode state (proto 2)
if (mDesktopTasksController != null) {
mDesktopTasksController.syncSurfaceState(info, finishTransaction);
return true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 4a55429..26c7394 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -111,6 +111,8 @@
@Mock
private BubbleLogger mBubbleLogger;
@Mock
+ private BubbleEducationController mEducationController;
+ @Mock
private ShellExecutor mMainExecutor;
@Captor
@@ -191,7 +193,7 @@
mPositioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner,
+ mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
mMainExecutor);
// Used by BubbleData to set lastAccessedTime
@@ -385,6 +387,65 @@
assertOverflowChangedTo(ImmutableList.of());
}
+ /**
+ * Verifies that the update shouldn't show the user education, if the education is not required
+ */
+ @Test
+ public void test_shouldNotShowEducation() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(false);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isFalse();
+ }
+
+ /**
+ * Verifies that the update should show the user education, if the education is required
+ */
+ @Test
+ public void test_shouldShowEducation() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(true);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isTrue();
+ }
+
+ /**
+ * Verifies that the update shouldn't show the user education, if the education is required but
+ * the bubble should auto-expand
+ */
+ @Test
+ public void test_shouldShowEducation_shouldAutoExpand() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(true);
+ mBubbleData.setListener(mListener);
+ mBubbleA1.setShouldAutoExpand(true);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isFalse();
+ }
+
// COLLAPSED / ADD
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index fe2da5d..ad84c7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -18,9 +18,9 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
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.eq;
@@ -146,7 +146,7 @@
public void testSnapToDismissStart() {
// verify it callbacks properly when the snap target indicates dismissing split.
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
+ SNAP_TO_START_AND_DISMISS);
mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
@@ -158,7 +158,7 @@
public void testSnapToDismissEnd() {
// verify it callbacks properly when the snap target indicates dismissing split.
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
+ SNAP_TO_END_AND_DISMISS);
mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6679f8f..e0f1f6e 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -18,6 +18,7 @@
#include <include/android/SkSurfaceAndroid.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/encode/SkPngEncoder.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkColorSpace.h>
@@ -440,6 +441,13 @@
procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
};
+ procs.fImageProc = [](SkImage* img, void* ctx) -> sk_sp<SkData> {
+ GrDirectContext* dCtx = static_cast<GrDirectContext*>(ctx);
+ return SkPngEncoder::Encode(dCtx,
+ img,
+ SkPngEncoder::Options{});
+ };
+ procs.fImageCtx = mRenderThread.getGrContext();
auto data = picture->serialize(&procs);
savePictureAsync(data, mCapturedFile);
mCaptureSequence = 0;
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index d6921c8..8c63580 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -17,9 +17,11 @@
package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -370,14 +372,14 @@
}
/**
- * Registers callback to be invoked when the {@link RouteListingPreference} of the target
- * router changes.
+ * Registers the given callback to be invoked when the {@link RouteListingPreference} of the
+ * target router changes.
*
- * <p>Calls using a previously registered callback will overwrite the callback record.
+ * <p>Calls using a previously registered callback will overwrite the previous executor.
*
* @see #setRouteListingPreference(RouteListingPreference)
- * @hide
*/
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
public void registerRouteListingPreferenceCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RouteListingPreferenceCallback routeListingPreferenceCallback) {
@@ -393,9 +395,8 @@
/**
* Unregisters the given callback to not receive {@link RouteListingPreference} change events.
- *
- * @hide
*/
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
public void unregisterRouteListingPreferenceCallback(
@NonNull RouteListingPreferenceCallback callback) {
Objects.requireNonNull(callback, "callback must not be null");
@@ -462,9 +463,12 @@
/**
* Returns the current {@link RouteListingPreference} of the target router.
*
+ * <p>If this instance was created using {@link #getInstance(Context, String)}, then it returns
+ * the last {@link RouteListingPreference} set by the process this router was created for.
+ *
* @see #setRouteListingPreference(RouteListingPreference)
- * @hide
*/
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
@Nullable
public RouteListingPreference getRouteListingPreference() {
synchronized (mLock) {
@@ -1201,9 +1205,19 @@
public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {}
}
- /** @hide */
+ /** Callback for receiving events related to {@link RouteListingPreference}. */
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
public abstract static class RouteListingPreferenceCallback {
- /** @hide */
+
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
+ public RouteListingPreferenceCallback() {}
+
+ /**
+ * Called when the {@link RouteListingPreference} changes.
+ *
+ * @see #getRouteListingPreference
+ */
+ @FlaggedApi(FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2)
public void onRouteListingPreferenceChanged(@Nullable RouteListingPreference preference) {}
}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
new file mode 100644
index 0000000..17962ee
--- /dev/null
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.media.flags"
+
+flag {
+ namespace: "media_solutions"
+ name: "enable_rlp_callbacks_in_media_router2"
+ description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
+ bug: "281067101"
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 4a252a9..471f3b9 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
@@ -32,7 +33,6 @@
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
-import com.android.settingslib.spa.gallery.page.ChartPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/BarChartEntry.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/BarChartEntry.kt
new file mode 100644
index 0000000..bf7a8e1
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/BarChartEntry.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.chart
+
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.widget.chart.BarChart
+import com.android.settingslib.spa.widget.chart.BarChartData
+import com.android.settingslib.spa.widget.chart.BarChartModel
+import com.android.settingslib.spa.widget.chart.ColorPalette
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.github.mikephil.charting.formatter.IAxisValueFormatter
+
+fun createBarChartEntry(owner: SettingsPage) = SettingsEntryBuilder.create("Bar Chart", owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = "Bar Chart"
+ })
+ BarChart(
+ barChartModel = object : BarChartModel {
+ override val chartDataList = listOf(
+ BarChartData(x = 0f, y = listOf(12f, 2f)),
+ BarChartData(x = 1f, y = listOf(5f, 1f)),
+ BarChartData(x = 2f, y = listOf(21f, 2f)),
+ BarChartData(x = 3f, y = listOf(5f, 1f)),
+ BarChartData(x = 4f, y = listOf(10f, 0f)),
+ BarChartData(x = 5f, y = listOf(9f, 1f)),
+ BarChartData(x = 6f, y = listOf(1f, 1f)),
+ )
+ override val colors = listOf(ColorPalette.green, ColorPalette.yellow)
+ override val xValueFormatter = IAxisValueFormatter { value, _ ->
+ "4/${value.toInt() + 1}"
+ }
+ override val yValueFormatter = IAxisValueFormatter { value, _ ->
+ "${value.toInt()}m"
+ }
+ override val yAxisMaxValue = 30f
+ }
+ )
+ }.build()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/ChartPageProvider.kt
similarity index 73%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/ChartPageProvider.kt
index 69c4705..7a6ae2c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/chart/ChartPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.chart
import android.os.Bundle
import androidx.compose.runtime.Composable
@@ -25,9 +25,6 @@
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.chart.BarChart
-import com.android.settingslib.spa.widget.chart.BarChartData
-import com.android.settingslib.spa.widget.chart.BarChartModel
import com.android.settingslib.spa.widget.chart.LineChart
import com.android.settingslib.spa.widget.chart.LineChartData
import com.android.settingslib.spa.widget.chart.LineChartModel
@@ -83,36 +80,7 @@
)
}.build()
)
- entryList.add(
- SettingsEntryBuilder.create("Bar Chart", owner)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Bar Chart"
- })
- BarChart(
- barChartModel = object : BarChartModel {
- override val chartDataList = listOf(
- BarChartData(x = 0f, y = 12f),
- BarChartData(x = 1f, y = 5f),
- BarChartData(x = 2f, y = 21f),
- BarChartData(x = 3f, y = 5f),
- BarChartData(x = 4f, y = 10f),
- BarChartData(x = 5f, y = 9f),
- BarChartData(x = 6f, y = 1f),
- )
- override val xValueFormatter =
- IAxisValueFormatter { value, _ ->
- "${WeekDay.values()[value.toInt()]}"
- }
- override val yValueFormatter =
- IAxisValueFormatter { value, _ ->
- "${value.toInt()}m"
- }
- override val yAxisMaxValue = 30f
- }
- )
- }.build()
- )
+ entryList.add(createBarChartEntry(owner))
entryList.add(
SettingsEntryBuilder.create("Pie Chart", owner)
.setUiLayoutFn {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index bb311a5..6cac220 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -28,12 +28,12 @@
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
-import com.android.settingslib.spa.gallery.page.ChartPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
index 27d270c..e6decb1 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.spa.screenshot
+import androidx.compose.material3.MaterialTheme
import com.android.settingslib.spa.widget.chart.BarChart
import com.android.settingslib.spa.widget.chart.BarChartData
import com.android.settingslib.spa.widget.chart.BarChartModel
@@ -45,17 +46,19 @@
@Test
fun test() {
screenshotRule.screenshotTest("barChart") {
+ val color = MaterialTheme.colorScheme.surfaceVariant
BarChart(
barChartModel = object : BarChartModel {
override val chartDataList = listOf(
- BarChartData(x = 0f, y = 12f),
- BarChartData(x = 1f, y = 5f),
- BarChartData(x = 2f, y = 21f),
- BarChartData(x = 3f, y = 5f),
- BarChartData(x = 4f, y = 10f),
- BarChartData(x = 5f, y = 9f),
- BarChartData(x = 6f, y = 1f),
+ BarChartData(x = 0f, y = listOf(12f)),
+ BarChartData(x = 1f, y = listOf(5f)),
+ BarChartData(x = 2f, y = listOf(21f)),
+ BarChartData(x = 3f, y = listOf(5f)),
+ BarChartData(x = 4f, y = listOf(10f)),
+ BarChartData(x = 5f, y = listOf(9f)),
+ BarChartData(x = 6f, y = listOf(1f)),
)
+ override val colors = listOf(color)
override val xValueFormatter =
IAxisValueFormatter { value, _ ->
"${WeekDay.values()[value.toInt()]}"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
index 0b0f07e..7ca15d9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt
@@ -31,6 +31,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
@@ -52,6 +53,11 @@
val chartDataList: List<BarChartData>
/**
+ * The color list for [BarChart].
+ */
+ val colors: List<Color>
+
+ /**
* The label text formatter for x value.
*/
val xValueFormatter: IAxisValueFormatter?
@@ -83,34 +89,14 @@
}
data class BarChartData(
- var x: Float?,
- var y: Float?,
+ var x: Float,
+ var y: List<Float>,
)
@Composable
fun BarChart(barChartModel: BarChartModel) {
- BarChart(
- chartDataList = barChartModel.chartDataList,
- xValueFormatter = barChartModel.xValueFormatter,
- yValueFormatter = barChartModel.yValueFormatter,
- yAxisMinValue = barChartModel.yAxisMinValue,
- yAxisMaxValue = barChartModel.yAxisMaxValue,
- yAxisLabelCount = barChartModel.yAxisLabelCount,
- )
-}
-
-@Composable
-fun BarChart(
- chartDataList: List<BarChartData>,
- modifier: Modifier = Modifier,
- xValueFormatter: IAxisValueFormatter? = null,
- yValueFormatter: IAxisValueFormatter? = null,
- yAxisMinValue: Float = 0f,
- yAxisMaxValue: Float = 30f,
- yAxisLabelCount: Int = 4,
-) {
Column(
- modifier = modifier
+ modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
@@ -126,50 +112,61 @@
val colorScheme = MaterialTheme.colorScheme
val labelTextColor = colorScheme.onSurfaceVariant.toArgb()
val labelTextSize = MaterialTheme.typography.bodyMedium.fontSize.value
- Crossfade(targetState = chartDataList) { barChartData ->
- AndroidView(factory = { context ->
- BarChart(context).apply {
- // Fixed Settings.
- layoutParams = LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- )
- this.description.isEnabled = false
- this.legend.isEnabled = false
- this.extraBottomOffset = 4f
- this.setScaleEnabled(false)
+ Crossfade(
+ targetState = barChartModel.chartDataList,
+ label = "chartDataList",
+ ) { barChartData ->
+ AndroidView(
+ factory = { context ->
+ BarChart(context).apply {
+ layoutParams = LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ )
+ description.isEnabled = false
+ legend.isEnabled = false
+ extraBottomOffset = 4f
+ setScaleEnabled(false)
- this.xAxis.position = XAxis.XAxisPosition.BOTTOM
- this.xAxis.setDrawGridLines(false)
- this.xAxis.setDrawAxisLine(false)
- this.xAxis.textColor = labelTextColor
- this.xAxis.textSize = labelTextSize
- this.xAxis.yOffset = 10f
+ xAxis.apply {
+ position = XAxis.XAxisPosition.BOTTOM
+ setDrawAxisLine(false)
+ setDrawGridLines(false)
+ textColor = labelTextColor
+ textSize = labelTextSize
+ valueFormatter = barChartModel.xValueFormatter
+ yOffset = 10f
+ }
- this.axisLeft.isEnabled = false
- this.axisRight.setDrawAxisLine(false)
- this.axisRight.textSize = labelTextSize
- this.axisRight.textColor = labelTextColor
- this.axisRight.gridColor = colorScheme.divider.toArgb()
- this.axisRight.xOffset = 10f
+ axisLeft.apply {
+ axisMaximum = barChartModel.yAxisMaxValue
+ axisMinimum = barChartModel.yAxisMinValue
+ isEnabled = false
+ }
- // Customizable Settings.
- this.xAxis.valueFormatter = xValueFormatter
- this.axisRight.valueFormatter = yValueFormatter
-
- this.axisLeft.axisMinimum = yAxisMinValue
- this.axisLeft.axisMaximum = yAxisMaxValue
- this.axisRight.axisMinimum = yAxisMinValue
- this.axisRight.axisMaximum = yAxisMaxValue
-
- this.axisRight.setLabelCount(yAxisLabelCount, true)
- }
- },
+ axisRight.apply {
+ axisMaximum = barChartModel.yAxisMaxValue
+ axisMinimum = barChartModel.yAxisMinValue
+ gridColor = colorScheme.divider.toArgb()
+ setDrawAxisLine(false)
+ setLabelCount(barChartModel.yAxisLabelCount, true)
+ textColor = labelTextColor
+ textSize = labelTextSize
+ valueFormatter = barChartModel.yValueFormatter
+ xOffset = 10f
+ }
+ }
+ },
modifier = Modifier
.wrapContentSize()
.padding(4.dp),
- update = {
- updateBarChartWithData(it, barChartData, colorScheme)
+ update = { barChart ->
+ updateBarChartWithData(
+ chart = barChart,
+ data = barChartData,
+ colorList = barChartModel.colors,
+ colorScheme = colorScheme,
+ )
}
)
}
@@ -177,26 +174,25 @@
}
}
-fun updateBarChartWithData(
+private fun updateBarChartWithData(
chart: BarChart,
data: List<BarChartData>,
+ colorList: List<Color>,
colorScheme: ColorScheme
) {
- val entries = ArrayList<BarEntry>()
- for (i in data.indices) {
- val item = data[i]
- entries.add(BarEntry(item.x ?: 0.toFloat(), item.y ?: 0.toFloat()))
+ val entries = data.map { item ->
+ BarEntry(item.x, item.y.toFloatArray())
}
- val ds = BarDataSet(entries, "")
- ds.colors = arrayListOf(colorScheme.surfaceVariant.toArgb())
- ds.setDrawValues(false)
- ds.isHighlightEnabled = true
- ds.highLightColor = colorScheme.primary.toArgb()
- ds.highLightAlpha = 255
+ val ds = BarDataSet(entries, "").apply {
+ colors = colorList.map(Color::toArgb)
+ setDrawValues(false)
+ isHighlightEnabled = true
+ highLightColor = colorScheme.primary.toArgb()
+ highLightAlpha = 255
+ }
// TODO: Sets round corners for bars.
- val d = BarData(ds)
- chart.data = d
+ chart.data = BarData(ds)
chart.invalidate()
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
index d7bbf08..ec43aab 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -18,20 +18,22 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.settingslib.spa.framework.theme.SettingsDimension
-import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
@Composable
@@ -48,6 +50,7 @@
expanded = expanded,
onExpandedChange = { expanded = it },
modifier = Modifier
+ .width(350.dp)
.padding(SettingsDimension.itemPadding),
) {
OutlinedTextField(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
index 6b3ae77..459a783 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -59,16 +59,16 @@
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
- modifier = Modifier.width(350.dp).padding(
- horizontal = SettingsDimension.itemPaddingEnd,
- vertical = SettingsDimension.itemPaddingVertical
- ).onSizeChanged {
- dropDownWidth = it.width
- },
+ modifier = Modifier
+ .width(350.dp)
+ .padding(SettingsDimension.itemPadding)
+ .onSizeChanged { dropDownWidth = it.width },
) {
OutlinedTextField(
// The `menuAnchor` modifier must be passed to the text field for correctness.
- modifier = Modifier.menuAnchor().fillMaxWidth(),
+ modifier = Modifier
+ .menuAnchor()
+ .fillMaxWidth(),
value = selectedOptionsState.joinToString(", "),
onValueChange = onselectedOptionStateChange,
label = { Text(text = label) },
@@ -83,12 +83,15 @@
if (options.isNotEmpty()) {
ExposedDropdownMenu(
expanded = expanded,
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier
+ .fillMaxWidth()
.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
onDismissRequest = { expanded = false },
) {
options.forEach { option ->
- TextButton(modifier = Modifier.fillMaxHeight().fillMaxWidth(), onClick = {
+ TextButton(modifier = Modifier
+ .fillMaxHeight()
+ .fillMaxWidth(), onClick = {
if (selectedOptionsState.contains(option)) {
selectedOptionsState.remove(
option
@@ -100,7 +103,9 @@
}
}) {
Row(
- modifier = Modifier.fillMaxHeight().fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxHeight()
+ .fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/chart/ChartTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/chart/ChartTest.kt
index 2230d6c..0fe755f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/chart/ChartTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/chart/ChartTest.kt
@@ -17,13 +17,17 @@
package com.android.settingslib.spa.widget.chart
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.assertContainsColor
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,22 +65,21 @@
@Test
fun bar_chart_displayed() {
composeTestRule.setContent {
- BarChart(
- chartDataList = listOf(
- BarChartData(x = 0f, y = 12f),
- BarChartData(x = 1f, y = 5f),
- BarChartData(x = 2f, y = 21f),
- BarChartData(x = 3f, y = 5f),
- BarChartData(x = 4f, y = 10f),
- BarChartData(x = 5f, y = 9f),
- BarChartData(x = 6f, y = 1f),
- ),
- yAxisMaxValue = 30f,
- modifier = Modifier.semantics { chart = "bar" }
- )
+ BarChart(object : BarChartModel {
+ override val chartDataList = listOf(
+ BarChartData(x = 0f, y = listOf(12f)),
+ BarChartData(x = 1f, y = listOf(5f)),
+ BarChartData(x = 2f, y = listOf(21f)),
+ BarChartData(x = 3f, y = listOf(5f)),
+ BarChartData(x = 4f, y = listOf(10f)),
+ BarChartData(x = 5f, y = listOf(9f)),
+ BarChartData(x = 6f, y = listOf(1f)),
+ )
+ override val colors = listOf(Color.Blue)
+ })
}
- composeTestRule.onNode(hasChart("bar")).assertIsDisplayed()
+ composeTestRule.onRoot().captureToImage().assertContainsColor(Color.Blue)
}
@Test
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ImageAssertions.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ImageAssertions.kt
new file mode 100644
index 0000000..0190989
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ImageAssertions.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.settingslib.spa.testutils
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.toPixelMap
+
+/**
+ * Asserts that the expected color is present in this bitmap.
+ *
+ * @throws AssertionError if the expected color is not present.
+ */
+fun ImageBitmap.assertContainsColor(expectedColor: Color) {
+ assert(containsColor(expectedColor)) {
+ "The given color $expectedColor was not found in the bitmap."
+ }
+}
+
+private fun ImageBitmap.containsColor(expectedColor: Color): Boolean {
+ val pixels = toPixelMap()
+ for (x in 0 until width) {
+ for (y in 0 until height) {
+ if (pixels[x, y] == expectedColor) return true
+ }
+ }
+ return false
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 755d971..fa8c1fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -4,9 +4,9 @@
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothCsipSetCoordinator;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -14,6 +14,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.net.Uri;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
@@ -23,6 +24,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import androidx.core.graphics.drawable.IconCompat;
import com.android.settingslib.R;
@@ -470,6 +472,136 @@
return extraTagValue(KEY_HEARABLE_CONTROL_SLICE, data);
}
+ /**
+ * Check if the Bluetooth device is an AvailableMediaBluetoothDevice, which means:
+ * 1) currently connected
+ * 2) is Hearing Aid or LE Audio
+ * OR
+ * 3) connected profile matches currentAudioProfile
+ *
+ * @param cachedDevice the CachedBluetoothDevice
+ * @param audioManager audio manager to get the current audio profile
+ * @return if the device is AvailableMediaBluetoothDevice
+ */
+ @WorkerThread
+ public static boolean isAvailableMediaBluetoothDevice(
+ CachedBluetoothDevice cachedDevice, AudioManager audioManager) {
+ int audioMode = audioManager.getMode();
+ int currentAudioProfile;
+
+ if (audioMode == AudioManager.MODE_RINGTONE
+ || audioMode == AudioManager.MODE_IN_CALL
+ || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ // in phone call
+ currentAudioProfile = BluetoothProfile.HEADSET;
+ } else {
+ // without phone call
+ currentAudioProfile = BluetoothProfile.A2DP;
+ }
+
+ boolean isFilterMatched = false;
+ if (isDeviceConnected(cachedDevice)) {
+ // If device is Hearing Aid or LE Audio, it is compatible with HFP and A2DP.
+ // It would show in Available Devices group.
+ if (cachedDevice.isConnectedAshaHearingAidDevice()
+ || cachedDevice.isConnectedLeAudioDevice()) {
+ Log.d(TAG, "isFilterMatched() device : "
+ + cachedDevice.getName() + ", the profile is connected.");
+ return true;
+ }
+ // According to the current audio profile type,
+ // this page will show the bluetooth device that have corresponding profile.
+ // For example:
+ // If current audio profile is a2dp, show the bluetooth device that have a2dp profile.
+ // If current audio profile is headset,
+ // show the bluetooth device that have headset profile.
+ switch (currentAudioProfile) {
+ case BluetoothProfile.A2DP:
+ isFilterMatched = cachedDevice.isConnectedA2dpDevice();
+ break;
+ case BluetoothProfile.HEADSET:
+ isFilterMatched = cachedDevice.isConnectedHfpDevice();
+ break;
+ }
+ }
+ return isFilterMatched;
+ }
+
+ /**
+ * Check if the Bluetooth device is a ConnectedBluetoothDevice, which means:
+ * 1) currently connected
+ * 2) is not Hearing Aid or LE Audio
+ * AND
+ * 3) connected profile does not match currentAudioProfile
+ *
+ * @param cachedDevice the CachedBluetoothDevice
+ * @param audioManager audio manager to get the current audio profile
+ * @return if the device is AvailableMediaBluetoothDevice
+ */
+ @WorkerThread
+ public static boolean isConnectedBluetoothDevice(
+ CachedBluetoothDevice cachedDevice, AudioManager audioManager) {
+ int audioMode = audioManager.getMode();
+ int currentAudioProfile;
+
+ if (audioMode == AudioManager.MODE_RINGTONE
+ || audioMode == AudioManager.MODE_IN_CALL
+ || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ // in phone call
+ currentAudioProfile = BluetoothProfile.HEADSET;
+ } else {
+ // without phone call
+ currentAudioProfile = BluetoothProfile.A2DP;
+ }
+
+ boolean isFilterMatched = false;
+ if (isDeviceConnected(cachedDevice)) {
+ // If device is Hearing Aid or LE Audio, it is compatible with HFP and A2DP.
+ // It would not show in Connected Devices group.
+ if (cachedDevice.isConnectedAshaHearingAidDevice()
+ || cachedDevice.isConnectedLeAudioDevice()) {
+ return false;
+ }
+ // According to the current audio profile type,
+ // this page will show the bluetooth device that doesn't have corresponding profile.
+ // For example:
+ // If current audio profile is a2dp,
+ // show the bluetooth device that doesn't have a2dp profile.
+ // If current audio profile is headset,
+ // show the bluetooth device that doesn't have headset profile.
+ switch (currentAudioProfile) {
+ case BluetoothProfile.A2DP:
+ isFilterMatched = !cachedDevice.isConnectedA2dpDevice();
+ break;
+ case BluetoothProfile.HEADSET:
+ isFilterMatched = !cachedDevice.isConnectedHfpDevice();
+ break;
+ }
+ }
+ return isFilterMatched;
+ }
+
+ /**
+ * Check if the Bluetooth device is an active media device
+ *
+ * @param cachedDevice the CachedBluetoothDevice
+ * @return if the Bluetooth device is an active media device
+ */
+ public static boolean isActiveMediaDevice(CachedBluetoothDevice cachedDevice) {
+ return cachedDevice.isActiveDevice(BluetoothProfile.A2DP)
+ || cachedDevice.isActiveDevice(BluetoothProfile.HEADSET)
+ || cachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)
+ || cachedDevice.isActiveDevice(BluetoothProfile.LE_AUDIO);
+ }
+
+ private static boolean isDeviceConnected(CachedBluetoothDevice cachedDevice) {
+ if (cachedDevice == null) {
+ return false;
+ }
+ final BluetoothDevice device = cachedDevice.getDevice();
+ return device.getBondState() == BluetoothDevice.BOND_BONDED && device.isConnected();
+ }
+
@SuppressLint("NewApi") // Hidden API made public
private static boolean doesClassMatch(BluetoothClass btClass, int classId) {
return btClass.doesClassMatch(classId);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 83972e8..7409eea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -25,6 +24,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.net.Uri;
import android.util.Pair;
@@ -46,6 +46,8 @@
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private AudioManager mAudioManager;
private Context mContext;
private static final String STRING_METADATA = "string_metadata";
@@ -255,4 +257,110 @@
public void isAdvancedUntetheredDevice_noMetadata_returnFalse() {
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
}
+
+ @Test
+ public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() {
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAvailableMediaBluetoothDevice_isHeadset_isConnectedA2dpDevice_returnFalse() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(false);
+ }
+
+ @Test
+ public void isAvailableMediaBluetoothDevice_isA2dp_isConnectedA2dpDevice_returnTrue() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAvailableMediaBluetoothDevice_isHeadset_isConnectedHfpDevice_returnTrue() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
+ when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(true);
+ }
+
+ @Test
+ public void isConnectedBluetoothDevice_isConnectedLeAudioDevice_returnFalse() {
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(false);
+ }
+
+ @Test
+ public void isConnectedBluetoothDevice_isHeadset_isConnectedA2dpDevice_returnTrue() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(true);
+ }
+
+ @Test
+ public void isConnectedBluetoothDevice_isA2dp_isConnectedA2dpDevice_returnFalse() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(false);
+ }
+
+ @Test
+ public void isConnectedBluetoothDevice_isHeadset_isConnectedHfpDevice_returnFalse() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
+ when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(true);
+
+ assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(false);
+ }
+
+ @Test
+ public void isConnectedBluetoothDevice_isNotConnected_returnFalse() {
+ when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mBluetoothDevice.isConnected()).thenReturn(false);
+
+ assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
+ mAudioManager)).isEqualTo(false);
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 94a3173..1c33544 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -242,6 +242,7 @@
Settings.Secure.HEARING_AID_MEDIA_ROUTING,
Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING,
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
- Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED
+ Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
+ Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 8179998..301fd6f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -201,6 +201,7 @@
VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SEARCH_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 7186aba..3c8d4bc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1933,6 +1933,9 @@
dumpSetting(s, p,
Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
SecureSettingsProto.Assist.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
+ SecureSettingsProto.Assist.SEARCH_LONG_PRESS_HOME_ENABLED);
p.end(assistToken);
final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b6bec1f..000612b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -240,6 +240,9 @@
/* Log fakes */
"tests/src/com/android/systemui/log/core/FakeLogBuffer.kt",
+
+ /* QS fakes */
+ "tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
],
path: "tests/src",
}
@@ -354,6 +357,11 @@
"tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt",
"tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt",
"tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt",
+
+ /* Quick Settings new pipeline converted tests */
+ "tests/src/com/android/systemui/qs/pipeline/data/**/*Test.kt",
+ "tests/src/com/android/systemui/qs/pipeline/domain/**/*Test.kt",
+ "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
],
path: "tests/src",
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 62c4424..b086ed8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1232,6 +1232,7 @@
<dimen name="magnification_setting_image_button_open_in_full_padding_horizontal">28dp</dimen>
<dimen name="magnification_setting_drag_corner_radius">28dp</dimen>
<dimen name="magnification_setting_drag_size">56dp</dimen>
+ <fraction name="magnification_resize_window_size_amount">10%</fraction>
<!-- Seekbar with icon buttons -->
<dimen name="seekbar_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 0d45422..0903463 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -38,9 +38,6 @@
protected. -->
<bool name="flag_battery_shield_icon">false</bool>
- <!-- Whether face auth will immediately stop when the display state is OFF -->
- <bool name="flag_stop_face_auth_on_display_off">true</bool>
-
<!-- Whether we want to stop pulsing while running the face scanning animation -->
<bool name="flag_stop_pulsing_face_scanning_animation">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e48901e..bd251bd 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -172,6 +172,10 @@
<item type="id" name="accessibility_action_move_right"/>
<item type="id" name="accessibility_action_move_up"/>
<item type="id" name="accessibility_action_move_down"/>
+ <item type="id" name="accessibility_action_increase_window_width"/>
+ <item type="id" name="accessibility_action_decrease_window_width"/>
+ <item type="id" name="accessibility_action_increase_window_height"/>
+ <item type="id" name="accessibility_action_decrease_window_height"/>
<!-- Accessibility actions for Accessibility floating menu. -->
<item type="id" name="action_move_top_left"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6840108..29c9767 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2403,6 +2403,16 @@
<string name="accessibility_control_move_left">Move left</string>
<!-- Action in accessibility menu to move the magnification window right. [CHAR LIMIT=30] -->
<string name="accessibility_control_move_right">Move right</string>
+
+ <!-- Action in accessibility menu to increase the magnification window width. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_increase_window_width">Increase width of magnifier</string>
+ <!-- Action in accessibility menu to decrease the magnification window width. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_decrease_window_width">Decrease width of magnifier</string>
+ <!-- Action in accessibility menu to increase the magnification window height. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_increase_window_height">Increase height of magnifier</string>
+ <!-- Action in accessibility menu to decrease the magnification window height. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_decrease_window_height">Decrease height of magnifier</string>
+
<!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_description">Magnification switch</string>
<!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0ba6c05..e4bbd3a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -35,7 +35,6 @@
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
-
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -161,8 +160,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -417,7 +414,6 @@
private final ActiveUnlockConfig mActiveUnlockConfig;
private final IDreamManager mDreamManager;
private final TelephonyManager mTelephonyManager;
- private final FeatureFlags mFeatureFlags;
@Nullable
private final FingerprintManager mFpm;
@Nullable
@@ -2365,7 +2361,6 @@
FaceWakeUpTriggersConfig faceWakeUpTriggersConfig,
DevicePostureController devicePostureController,
Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
- FeatureFlags featureFlags,
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
DisplayTracker displayTracker) {
@@ -2400,7 +2395,6 @@
mPackageManager = packageManager;
mFpm = fingerprintManager;
mFaceManager = faceManager;
- mFeatureFlags = featureFlags;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mFaceAcquiredInfoIgnoreList = Arrays.stream(
mContext.getResources().getIntArray(
@@ -2417,9 +2411,7 @@
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
mDisplayTracker = displayTracker;
- if (mFeatureFlags.isEnabled(Flags.STOP_FACE_AUTH_ON_DISPLAY_OFF)) {
- mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
- }
+ mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
mHandler = new Handler(mainLooper) {
@Override
@@ -4205,21 +4197,19 @@
@Override
public void onTaskStackChangedBackground() {
try {
- if (mFeatureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
- RootTaskInfo standardTask = mActivityTaskManager.getRootTaskInfo(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
- mAllowFingerprintOnCurrentOccludingActivity =
- standardTask.topActivity != null
- && !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
- && mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
- standardTask.topActivity.getPackageName())
- && standardTask.visible;
- if (mAllowFingerprintOnCurrentOccludingActivity != previousState) {
- mLogger.allowFingerprintOnCurrentOccludingActivityChanged(
- mAllowFingerprintOnCurrentOccludingActivity);
- updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
- }
+ RootTaskInfo standardTask = mActivityTaskManager.getRootTaskInfo(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
+ mAllowFingerprintOnCurrentOccludingActivity =
+ standardTask.topActivity != null
+ && !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
+ && mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
+ standardTask.topActivity.getPackageName())
+ && standardTask.visible;
+ if (mAllowFingerprintOnCurrentOccludingActivity != previousState) {
+ mLogger.allowFingerprintOnCurrentOccludingActivityChanged(
+ mAllowFingerprintOnCurrentOccludingActivity);
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
RootTaskInfo assistantTask = mActivityTaskManager.getRootTaskInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index f1cebba..773241e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -26,6 +26,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -719,6 +720,18 @@
}
/**
+ * Sets the window frame size with given width and height in pixels without changing the
+ * window center.
+ *
+ * @param width the window frame width in pixels
+ * @param height the window frame height in pixels.
+ */
+ @MainThread
+ private void setMagnificationFrameSize(int width, int height) {
+ setWindowSize(width + 2 * mMirrorSurfaceMargin, height + 2 * mMirrorSurfaceMargin);
+ }
+
+ /**
* Sets the window size with given width and height in pixels without changing the
* window center. The width or the height will be clamped in the range
* [{@link #mMinWindowSize}, screen width or height].
@@ -1496,19 +1509,50 @@
AccessibilityAction.ACTION_CLICK.getId(), getClickAccessibilityActionLabel());
info.addAction(clickAction);
info.setClickable(true);
+
info.addAction(
new AccessibilityAction(R.id.accessibility_action_zoom_in,
mContext.getString(R.string.accessibility_control_zoom_in)));
info.addAction(new AccessibilityAction(R.id.accessibility_action_zoom_out,
mContext.getString(R.string.accessibility_control_zoom_out)));
- info.addAction(new AccessibilityAction(R.id.accessibility_action_move_up,
- mContext.getString(R.string.accessibility_control_move_up)));
- info.addAction(new AccessibilityAction(R.id.accessibility_action_move_down,
- mContext.getString(R.string.accessibility_control_move_down)));
- info.addAction(new AccessibilityAction(R.id.accessibility_action_move_left,
- mContext.getString(R.string.accessibility_control_move_left)));
- info.addAction(new AccessibilityAction(R.id.accessibility_action_move_right,
- mContext.getString(R.string.accessibility_control_move_right)));
+
+ if (!mEditSizeEnable) {
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_up,
+ mContext.getString(R.string.accessibility_control_move_up)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_down,
+ mContext.getString(R.string.accessibility_control_move_down)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_left,
+ mContext.getString(R.string.accessibility_control_move_left)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_right,
+ mContext.getString(R.string.accessibility_control_move_right)));
+ } else {
+ if ((mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin)
+ < mWindowBounds.width()) {
+ info.addAction(new AccessibilityAction(
+ R.id.accessibility_action_increase_window_width,
+ mContext.getString(
+ R.string.accessibility_control_increase_window_width)));
+ }
+ if ((mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin)
+ < mWindowBounds.height()) {
+ info.addAction(new AccessibilityAction(
+ R.id.accessibility_action_increase_window_height,
+ mContext.getString(
+ R.string.accessibility_control_increase_window_height)));
+ }
+ if ((mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin) > mMinWindowSize) {
+ info.addAction(new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_width,
+ mContext.getString(
+ R.string.accessibility_control_decrease_window_width)));
+ }
+ if ((mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin) > mMinWindowSize) {
+ info.addAction(new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_height,
+ mContext.getString(
+ R.string.accessibility_control_decrease_window_height)));
+ }
+ }
info.setContentDescription(mContext.getString(R.string.magnification_window_title));
info.setStateDescription(formatStateDescription(getScale()));
@@ -1523,6 +1567,11 @@
}
private boolean performA11yAction(int action) {
+ final float changeWindowSizeAmount = mContext.getResources().getFraction(
+ R.fraction.magnification_resize_window_size_amount,
+ /* base= */ 1,
+ /* pbase= */ 1);
+
if (action == AccessibilityAction.ACTION_CLICK.getId()) {
if (mEditSizeEnable) {
// When edit mode is enabled, click the magnifier to exit edit mode.
@@ -1544,9 +1593,26 @@
move(-mSourceBounds.width(), 0);
} else if (action == R.id.accessibility_action_move_right) {
move(mSourceBounds.width(), 0);
+ } else if (action == R.id.accessibility_action_increase_window_width) {
+ int newFrameWidth =
+ (int) (mMagnificationFrame.width() * (1 + changeWindowSizeAmount));
+ setMagnificationFrameSize(newFrameWidth, mMagnificationFrame.height());
+ } else if (action == R.id.accessibility_action_increase_window_height) {
+ int newFrameHeight =
+ (int) (mMagnificationFrame.height() * (1 + changeWindowSizeAmount));
+ setMagnificationFrameSize(mMagnificationFrame.width(), newFrameHeight);
+ } else if (action == R.id.accessibility_action_decrease_window_width) {
+ int newFrameWidth =
+ (int) (mMagnificationFrame.width() * (1 - changeWindowSizeAmount));
+ setMagnificationFrameSize(newFrameWidth, mMagnificationFrame.height());
+ } else if (action == R.id.accessibility_action_decrease_window_height) {
+ int newFrameHeight =
+ (int) (mMagnificationFrame.height() * (1 - changeWindowSizeAmount));
+ setMagnificationFrameSize(mMagnificationFrame.width(), newFrameHeight);
} else {
return false;
}
+
mWindowMagnifierCallback.onAccessibilityActionPerformed(mDisplayId);
return true;
}
@@ -1557,4 +1623,5 @@
mDisplayId, scale, /* updatePersistence= */ true);
}
}
+
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 94b5fb2..b8c0e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -18,14 +18,21 @@
import android.animation.ValueAnimator
import android.graphics.PointF
import android.graphics.RectF
-import com.android.systemui.Dumpable
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
+import com.android.systemui.Dumpable
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.ViewController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import java.io.PrintWriter
/**
@@ -41,7 +48,7 @@
abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
view: T,
protected val statusBarStateController: StatusBarStateController,
- protected val shadeExpansionStateManager: ShadeExpansionStateManager,
+ protected val primaryBouncerInteractor: PrimaryBouncerInteractor,
protected val dialogManager: SystemUIDialogManager,
private val dumpManager: DumpManager
) : ViewController<T>(view), Dumpable {
@@ -54,14 +61,6 @@
private var dialogAlphaAnimator: ValueAnimator? = null
private val dialogListener = SystemUIDialogManager.Listener { runDialogAlphaAnimator() }
- private val shadeExpansionListener = ShadeExpansionListener { event ->
- // Notification shade can be expanded but not visible (fraction: 0.0), for example
- // when a heads-up notification (HUN) is showing.
- notificationShadeVisible = event.expanded && event.fraction > 0f
- view.onExpansionChanged(event.fraction)
- updatePauseAuth()
- }
-
/** If the notification shade is visible. */
var notificationShadeVisible: Boolean = false
@@ -88,6 +87,28 @@
view.updateAlpha()
}
+ init {
+ view.repeatWhenAttached {
+ // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
+ // can make the view not visible; and we still want to listen for events
+ // that may make the view visible again.
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ listenForBouncerExpansion(this)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ open suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+ return scope.launch {
+ primaryBouncerInteractor.bouncerExpansion.map { 1f - it }.collect { expansion: Float ->
+ notificationShadeVisible = expansion > 0f
+ view.onExpansionChanged(expansion)
+ updatePauseAuth()
+ }
+ }
+ }
+
fun runDialogAlphaAnimator() {
val hideAffordance = dialogManager.shouldHideAffordance()
dialogAlphaAnimator?.cancel()
@@ -108,15 +129,11 @@
}
override fun onViewAttached() {
- val currentState =
- shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
- shadeExpansionListener.onPanelExpansionChanged(currentState)
dialogManager.registerListener(dialogListener)
dumpManager.registerDumpable(dumpTag, this)
}
override fun onViewDetached() {
- shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
dialogManager.unregisterListener(dialogListener)
dumpManager.unregisterDumpable(dumpTag)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
index 802eea3..03749a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.biometrics
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
/**
@@ -26,13 +26,13 @@
class UdfpsBpViewController(
view: UdfpsBpView,
statusBarStateController: StatusBarStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
systemUIDialogManager: SystemUIDialogManager,
dumpManager: DumpManager
) : UdfpsAnimationViewController<UdfpsBpView>(
view,
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
systemUIDialogManager,
dumpManager
) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a368703..11a578b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -23,7 +23,6 @@
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
-
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
@@ -60,6 +59,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -92,7 +92,6 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -118,6 +117,7 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and toggles the UDFPS display mode.
@@ -149,7 +149,6 @@
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
@NonNull private final Executor mBiometricExecutor;
- @NonNull private final ShadeExpansionStateManager mShadeExpansionStateManager;
@NonNull private final StatusBarStateController mStatusBarStateController;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@@ -264,23 +263,44 @@
}
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
+ @OptIn(markerClass = ExperimentalCoroutinesApi.class)
@Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@NonNull IUdfpsOverlayControllerCallback callback) {
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
- new UdfpsControllerOverlay(mContext, mFingerprintManager, mInflater,
- mWindowManager, mAccessibilityManager, mStatusBarStateController,
- mShadeExpansionStateManager, mKeyguardViewManager,
- mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
- mLockscreenShadeTransitionController, mConfigurationController,
- mKeyguardStateController,
- mUnlockedScreenOffAnimationController,
- mUdfpsDisplayMode, mSecureSettings, requestId, reason, callback,
- (view, event, fromUdfpsView) -> onTouch(requestId, event,
- fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
- mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils,
- mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels)));
+ new UdfpsControllerOverlay(
+ mContext,
+ mFingerprintManager,
+ mInflater,
+ mWindowManager,
+ mAccessibilityManager,
+ mStatusBarStateController,
+ mKeyguardViewManager,
+ mKeyguardUpdateMonitor,
+ mDialogManager,
+ mDumpManager,
+ mLockscreenShadeTransitionController,
+ mConfigurationController,
+ mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
+ mUdfpsDisplayMode,
+ mSecureSettings,
+ requestId,
+ reason,
+ callback,
+ (view, event, fromUdfpsView) -> onTouch(
+ requestId,
+ event,
+ fromUdfpsView
+ ),
+ mActivityLaunchAnimator,
+ mFeatureFlags,
+ mPrimaryBouncerInteractor,
+ mAlternateBouncerInteractor,
+ mUdfpsUtils,
+ mUdfpsKeyguardAccessibilityDelegate,
+ mUdfpsKeyguardViewModels
+ )));
}
@Override
@@ -814,7 +834,6 @@
@NonNull WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull ShadeExpansionStateManager shadeExpansionStateManager,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -859,7 +878,6 @@
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
- mShadeExpansionStateManager = shadeExpansionStateManager;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index d6ef94d..4031f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -60,7 +60,6 @@
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -90,7 +89,6 @@
private val windowManager: WindowManager,
private val accessibilityManager: AccessibilityManager,
private val statusBarStateController: StatusBarStateController,
- private val shadeExpansionStateManager: ShadeExpansionStateManager,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dialogManager: SystemUIDialogManager,
@@ -245,7 +243,7 @@
updateAccessibilityViewLocation(sensorBounds)
},
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
dialogManager,
dumpManager
)
@@ -256,7 +254,7 @@
UdfpsKeyguardViewController(
view.addUdfpsView(R.layout.udfps_keyguard_view),
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
dialogManager,
dumpManager,
alternateBouncerInteractor,
@@ -268,7 +266,6 @@
updateSensorLocation(sensorBounds)
},
statusBarStateController,
- shadeExpansionStateManager,
statusBarKeyguardViewManager,
keyguardUpdateMonitor,
dumpManager,
@@ -291,7 +288,7 @@
UdfpsBpViewController(
view.addUdfpsView(R.layout.udfps_bp_view),
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
dialogManager,
dumpManager
)
@@ -301,7 +298,7 @@
UdfpsFpmEmptyViewController(
view.addUdfpsView(R.layout.udfps_fpm_empty_view),
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
dialogManager,
dumpManager
)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
index d122d64..88002e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.biometrics
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
/**
@@ -28,13 +28,13 @@
class UdfpsFpmEmptyViewController(
view: UdfpsFpmEmptyView,
statusBarStateController: StatusBarStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
systemUIDialogManager: SystemUIDialogManager,
dumpManager: DumpManager
) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
view,
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
systemUIDialogManager,
dumpManager
) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index e3fd3ce1..84a746c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -37,8 +37,6 @@
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -55,11 +53,9 @@
import kotlinx.coroutines.launch
/** Class that coordinates non-HBM animations during keyguard authentication. */
-open class UdfpsKeyguardViewControllerLegacy
-constructor(
+open class UdfpsKeyguardViewControllerLegacy(
private val view: UdfpsKeyguardViewLegacy,
statusBarStateController: StatusBarStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
private val keyguardViewManager: StatusBarKeyguardViewManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
dumpManager: DumpManager,
@@ -71,14 +67,14 @@
private val udfpsController: UdfpsController,
private val activityLaunchAnimator: ActivityLaunchAnimator,
featureFlags: FeatureFlags,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
systemUIDialogManager,
dumpManager,
),
@@ -159,17 +155,6 @@
}
}
- private val shadeExpansionListener = ShadeExpansionListener { (fraction) ->
- panelExpansionFraction =
- if (keyguardViewManager.isPrimaryBouncerInTransit) {
- aboutToShowBouncerProgress(fraction)
- } else {
- fraction
- }
- updateAlpha()
- updatePauseAuth()
- }
-
private val keyguardStateControllerCallback: KeyguardStateController.Callback =
object : KeyguardStateController.Callback {
override fun onUnlockedChanged() {
@@ -262,10 +247,17 @@
}
@VisibleForTesting
- suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+ override suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
return scope.launch {
primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
inputBouncerExpansion = bouncerExpansion
+
+ panelExpansionFraction =
+ if (keyguardViewManager.isPrimaryBouncerInTransit) {
+ aboutToShowBouncerProgress(1f - bouncerExpansion)
+ } else {
+ 1f - bouncerExpansion
+ }
updateAlpha()
updatePauseAuth()
}
@@ -295,8 +287,6 @@
qsExpansion = keyguardViewManager.qsExpansion
keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
configurationController.addCallback(configurationListener)
- val currentState = shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
- shadeExpansionListener.onPanelExpansionChanged(currentState)
updateScaleFactor()
view.updatePadding()
updateAlpha()
@@ -321,7 +311,6 @@
keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI)
keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
configurationController.removeCallback(configurationListener)
- shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
index c9b1624..6f4e1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
@@ -19,11 +19,11 @@
import com.android.systemui.biometrics.UdfpsAnimationViewController
import com.android.systemui.biometrics.UdfpsKeyguardView
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,7 +32,7 @@
open class UdfpsKeyguardViewController(
val view: UdfpsKeyguardView,
statusBarStateController: StatusBarStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
systemUIDialogManager: SystemUIDialogManager,
dumpManager: DumpManager,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -41,7 +41,7 @@
UdfpsAnimationViewController<UdfpsKeyguardView>(
view,
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
systemUIDialogManager,
dumpManager,
),
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index c9517c2..da5e933 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -29,6 +29,7 @@
import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.StatsManager;
+import android.app.StatusBarManager;
import android.app.UiModeManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
@@ -681,4 +682,10 @@
static TextClassificationManager provideTextClassificationManager(Context context) {
return context.getSystemService(TextClassificationManager.class);
}
+
+ @Provides
+ @Singleton
+ static StatusBarManager provideStatusBarManager(Context context) {
+ return context.getSystemService(StatusBarManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 1cd3774..4e4b79c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -155,9 +155,12 @@
return true;
}
- // Don't set expansion if the user doesn't have a pin/password set.
+ // Don't set expansion if the user doesn't have a pin/password set so that no
+ // animations are played we're not transitioning to the bouncer.
if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
- return true;
+ // Return false so the gesture is not consumed, allowing the dream to wake
+ // if it wants instead of doing nothing.
+ return false;
}
// For consistency, we adopt the expansion definition found in the
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2cc07fd..312f5b7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -268,10 +268,6 @@
val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
- /** Whether to listen for fingerprint authentication over keyguard occluding activities. */
- // TODO(b/283260512): Tracking bug.
- @JvmField val FP_LISTEN_OCCLUDING_APPS = releasedFlag("fp_listen_occluding_apps")
-
/** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
// TODO(b/286563884): Tracking bug
@JvmField val KEYGUARD_TALKBACK_FIX = releasedFlag("keyguard_talkback_fix")
@@ -312,11 +308,6 @@
val KEYGUARD_WM_STATE_REFACTOR: UnreleasedFlag =
unreleasedFlag("keyguard_wm_state_refactor")
- /** Stop running face auth when the display state changes to OFF. */
- // TODO(b/294221702): Tracking bug.
- @JvmField val STOP_FACE_AUTH_ON_DISPLAY_OFF = resourceBooleanFlag(
- R.bool.flag_stop_face_auth_on_display_off, "stop_face_auth_on_display_off")
-
/** Flag to disable the face scanning animation pulsing. */
// TODO(b/295245791): Tracking bug.
@JvmField val STOP_PULSING_FACE_SCANNING_ANIMATION = resourceBooleanFlag(
@@ -785,7 +776,8 @@
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
- val ENABLE_CLOCK_KEYGUARD_PRESENTATION = unreleasedFlag("enable_clock_keyguard_presentation")
+ val ENABLE_CLOCK_KEYGUARD_PRESENTATION =
+ unreleasedFlag("enable_clock_keyguard_presentation", teamfood = true)
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
index 3ec660a..f6ad829 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -22,8 +22,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
@@ -60,7 +58,6 @@
private val context: Context,
activityStarter: ActivityStarter,
powerInteractor: PowerInteractor,
- featureFlags: FeatureFlags,
) {
private val keyguardOccludedByApp: Flow<Boolean> =
combine(
@@ -93,37 +90,35 @@
.ifKeyguardOccludedByApp(/* elseFlow */ flowOf(null))
init {
- if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
- scope.launch {
- // On fingerprint success when the screen is on, go to the home screen
- fingerprintUnlockSuccessEvents.sample(powerInteractor.isInteractive).collect {
- if (it) {
- goToHomeScreen()
- }
- // don't go to the home screen if the authentication is from AOD/dozing/off
+ scope.launch {
+ // On fingerprint success when the screen is on, go to the home screen
+ fingerprintUnlockSuccessEvents.sample(powerInteractor.isInteractive).collect {
+ if (it) {
+ goToHomeScreen()
}
+ // don't go to the home screen if the authentication is from AOD/dozing/off
}
+ }
- scope.launch {
- // On device fingerprint lockout, request the bouncer with a runnable to
- // go to the home screen. Without this, the bouncer won't proceed to the home
- // screen.
- fingerprintLockoutEvents.collect {
- activityStarter.dismissKeyguardThenExecute(
- object : ActivityStarter.OnDismissAction {
- override fun onDismiss(): Boolean {
- goToHomeScreen()
- return false
- }
+ scope.launch {
+ // On device fingerprint lockout, request the bouncer with a runnable to
+ // go to the home screen. Without this, the bouncer won't proceed to the home
+ // screen.
+ fingerprintLockoutEvents.collect {
+ activityStarter.dismissKeyguardThenExecute(
+ object : ActivityStarter.OnDismissAction {
+ override fun onDismiss(): Boolean {
+ goToHomeScreen()
+ return false
+ }
- override fun willRunAnimationOnKeyguard(): Boolean {
- return false
- }
- },
- /* cancel= */ null,
- /* afterKeyguardGone */ false
- )
- }
+ override fun willRunAnimationOnKeyguard(): Boolean {
+ return false
+ }
+ },
+ /* cancel= */ null,
+ /* afterKeyguardGone */ false
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 9503f2c..4b76821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -59,20 +59,18 @@
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
- launch {
- occludingAppDeviceEntryMessageViewModel.message.collect {
- biometricMessage ->
- if (biometricMessage?.message != null) {
- chipbarCoordinator.displayView(
- createChipbarInfo(
- biometricMessage.message,
- R.drawable.ic_lock,
- )
+ launch {
+ occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
+ ->
+ if (biometricMessage?.message != null) {
+ chipbarCoordinator.displayView(
+ createChipbarInfo(
+ biometricMessage.message,
+ R.drawable.ic_lock,
)
- } else {
- chipbarCoordinator.removeView(ID, "occludingAppMsgNull")
- }
+ )
+ } else {
+ chipbarCoordinator.removeView(ID, "occludingAppMsgNull")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 8945318..85a2fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -264,14 +264,8 @@
KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
context.resources,
)
-
val startPadding: Int =
- with(context.resources) {
- getDimensionPixelSize(
- com.android.systemui.customization.R.dimen.clock_padding_start
- ) + getDimensionPixelSize(R.dimen.below_clock_padding_start)
- }
-
+ context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start)
val endPadding: Int =
context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 4be572f..d403788 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -29,6 +29,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -72,6 +73,7 @@
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
+ private final StatusBarManager mStatusBarManager;
private String mPackageName;
private int mUid;
@@ -87,9 +89,11 @@
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
- Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
+ Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
+ StatusBarManager statusBarManager) {
mFeatureFlags = featureFlags;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
+ mStatusBarManager = statusBarManager;
}
@Override
@@ -311,6 +315,8 @@
// WM Shell running inside.
mUserSelectingTask = true;
startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+ // close shade if it's open
+ mStatusBarManager.collapsePanels();
}
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index 98f2fee..5154067 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -16,10 +16,8 @@
package com.android.systemui.screenshot
-import android.graphics.Insets
import android.util.Log
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotRequest
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -49,64 +47,6 @@
/** For the Java Async version, to invoke the callback. */
@Application private val mainScope: CoroutineScope
) : ScreenshotRequestProcessor {
- /**
- * Inspects the incoming request, returning a potentially modified request depending on policy.
- *
- * @param request the request to process
- */
- // TODO: Delete once SCREENSHOT_METADATA flag is launched
- suspend fun process(request: ScreenshotRequest): ScreenshotRequest {
- var result = request
-
- // Apply work profile screenshots policy:
- //
- // If the focused app belongs to a work profile, transforms a full screen
- // (or partial) screenshot request to a task snapshot (provided image) screenshot.
-
- // Whenever displayContentInfo is fetched, the topComponent is also populated
- // regardless of the managed profile status.
-
- if (request.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
- Log.d(TAG, "findPrimaryContent: $info")
-
- result = if (policy.isManagedProfile(info.user.identifier)) {
- val image = capture.captureTask(info.taskId)
- ?: error("Task snapshot returned a null Bitmap!")
-
- // Provide the task snapshot as the screenshot
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, request.source)
- .setTopComponent(info.component)
- .setTaskId(info.taskId)
- .setUserId(info.user.identifier)
- .setBitmap(image)
- .setBoundsOnScreen(info.bounds)
- .setInsets(Insets.NONE)
- .build()
- } else {
- // Create a new request of the same type which includes the top component
- ScreenshotRequest.Builder(request.type, request.source)
- .setTopComponent(info.component).build()
- }
- }
-
- return result
- }
-
- /**
- * Note: This is for compatibility with existing Java. Prefer the suspending function when
- * calling from a Coroutine context.
- *
- * @param request the request to process
- * @param callback the callback to provide the processed request, invoked from the main thread
- */
- // TODO: Delete once SCREENSHOT_METADATA flag is launched
- fun processAsync(request: ScreenshotRequest, callback: Consumer<ScreenshotRequest>) {
- mainScope.launch {
- val result = process(request)
- callback.accept(result)
- }
- }
override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
var result = screenshot
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 6c886fc..c5bc2fb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -4,6 +4,7 @@
import android.os.Trace
import android.util.Log
import android.view.Display
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.dagger.SysUISingleton
@@ -55,7 +56,7 @@
onSaved: (Uri) -> Unit,
requestCallback: RequestCallback
) {
- val displayIds = getDisplaysToScreenshot()
+ val displayIds = getDisplaysToScreenshot(screenshotRequest.type)
val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
screenshotRequest.oneForEachDisplay(displayIds).forEach { screenshotData: ScreenshotData ->
dispatchToController(
@@ -93,8 +94,13 @@
.handleScreenshot(screenshotData, onSaved, callback)
}
- private fun getDisplaysToScreenshot(): List<Int> {
- return displays.value.filter { it.type in ALLOWED_DISPLAY_TYPES }.map { it.displayId }
+ private fun getDisplaysToScreenshot(requestType: Int): List<Int> {
+ return if (requestType == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ // If this is a provided image, let's show the UI on the default display only.
+ listOf(Display.DEFAULT_DISPLAY)
+ } else {
+ displays.value.filter { it.type in ALLOWED_DISPLAY_TYPES }.map { it.displayId }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 509921f..947259a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -42,7 +42,7 @@
/**
* The amount the lockscreen shade has dragged down by the user, [0-1]. 0 means fully collapsed,
- * 1 means fully expanded.
+ * 1 means fully expanded. Value resets to 0 when the user finishes dragging.
*/
val lockscreenShadeExpansion: StateFlow<Float>
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 3b19411..95a072c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -20,6 +20,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -28,22 +32,29 @@
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
+import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Business logic for shade interactions. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class ShadeInteractor
@Inject
constructor(
@Application scope: CoroutineScope,
disableFlagsRepository: DisableFlagsRepository,
+ sceneContainerFlags: SceneContainerFlags,
+ sceneInteractorProvider: Provider<SceneInteractor>,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
@@ -68,28 +79,45 @@
/** The amount [0-1] that the shade has been opened */
val shadeExpansion: Flow<Float> =
- combine(
- repository.lockscreenShadeExpansion,
- keyguardRepository.statusBarState,
- repository.legacyShadeExpansion,
- repository.qsExpansion,
- splitShadeEnabled
- ) { dragDownAmount, statusBarState, legacyShadeExpansion, qsExpansion, splitShadeEnabled ->
- when (statusBarState) {
- // legacyShadeExpansion is 1 instead of 0 when QS is expanded
- StatusBarState.SHADE ->
- if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
- StatusBarState.KEYGUARD -> dragDownAmount
- // This is required, as shadeExpansion gets reset to 0f even with the shade open
- StatusBarState.SHADE_LOCKED -> 1f
- }
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.Shade)
+ } else {
+ combine(
+ repository.lockscreenShadeExpansion,
+ keyguardRepository.statusBarState,
+ repository.legacyShadeExpansion,
+ repository.qsExpansion,
+ splitShadeEnabled
+ ) {
+ lockscreenShadeExpansion,
+ statusBarState,
+ legacyShadeExpansion,
+ qsExpansion,
+ splitShadeEnabled ->
+ when (statusBarState) {
+ // legacyShadeExpansion is 1 instead of 0 when QS is expanded
+ StatusBarState.SHADE ->
+ if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
+ StatusBarState.KEYGUARD -> lockscreenShadeExpansion
+ // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
+ // the pointer is lifted and the lockscreen shade is fully expanded
+ StatusBarState.SHADE_LOCKED -> 1f
+ }
+ }
+ .distinctUntilChanged()
}
/**
* The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
* report 0f.
*/
- val qsExpansion: StateFlow<Float> = repository.qsExpansion
+ val qsExpansion: StateFlow<Float> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+ .stateIn(scope, SharingStarted.Eagerly, 0f)
+ } else {
+ repository.qsExpansion
+ }
/** The amount [0-1] either QS or the shade has been opened */
val anyExpansion: StateFlow<Float> =
@@ -119,4 +147,26 @@
disableFlags.isQuickSettingsEnabled() &&
!isDozing
}
+
+ fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ if (state.scene == sceneKey) {
+ flowOf(1f)
+ } else {
+ flowOf(0f)
+ }
+ is ObservableTransitionState.Transition ->
+ if (state.toScene == sceneKey) {
+ state.progress
+ } else if (state.fromScene == sceneKey) {
+ state.progress.map { progress -> 1 - progress }
+ } else {
+ flowOf(0f)
+ }
+ }
+ }
+ .distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d8f513c..93b5ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1659,8 +1659,9 @@
@VisibleForTesting
void onKeyguardTransitionChanged(TransitionStep transitionStep) {
- boolean isTransitionToAod = transitionStep.getFrom().equals(KeyguardState.GONE)
- && transitionStep.getTo().equals(KeyguardState.AOD);
+ boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD)
+ && (transitionStep.getFrom().equals(KeyguardState.GONE)
+ || transitionStep.getFrom().equals(KeyguardState.OCCLUDED));
if (mIsInTransitionToAod != isTransitionToAod) {
mIsInTransitionToAod = isTransitionToAod;
updateShowEmptyShadeView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 6f4adeb..f750fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -44,7 +44,13 @@
shadeInteractor: ShadeInteractor,
) {
private val statesForConstrainedNotifications =
- setOf(KeyguardState.LOCKSCREEN, KeyguardState.AOD, KeyguardState.DOZING)
+ setOf(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.AOD,
+ KeyguardState.DOZING,
+ KeyguardState.ALTERNATE_BOUNCER,
+ KeyguardState.PRIMARY_BOUNCER
+ )
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
interactor.configurationBasedDimensions
@@ -126,6 +132,7 @@
/**
* When on keyguard, there is limited space to display notifications so calculate how many could
* be shown. Otherwise, there is no limit since the vertical space will be scrollable.
+ *
* TODO: b/296606746 - Need to rerun logic when notifs change
*/
val maxNotifications: Flow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 0031b574..3b9afa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
-
-import static com.android.systemui.flags.Flags.FP_LISTEN_OCCLUDING_APPS;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.keyguard.WakefulnessLifecycle.UNKNOWN_LAST_WAKE_TIME;
@@ -702,8 +700,7 @@
}
final boolean screenOff = !mUpdateMonitor.isDeviceInteractive();
- if (!mVibratorHelper.hasVibrator() && (screenOff || (mUpdateMonitor.isDreaming()
- && !mFeatureFlags.isEnabled(FP_LISTEN_OCCLUDING_APPS)))) {
+ if (!mVibratorHelper.hasVibrator() && screenOff) {
mLogger.d("wakeup device on authentication failure (device doesn't have a vibrator)");
startWakeAndUnlock(MODE_ONLY_WAKE);
} else if (biometricSourceType == BiometricSourceType.FINGERPRINT
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index ecc996c..53662f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -53,8 +53,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
@@ -116,7 +116,7 @@
NotificationMediaManager notificationMediaManager,
NotificationGutsManager notificationGutsManager,
InitController initController,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
+ VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
NotificationRemoteInputManager remoteInputManager,
NotificationRemoteInputManager.Callback remoteInputManagerCallback,
NotificationListContainer notificationListContainer) {
@@ -162,7 +162,7 @@
initController.addPostInitTask(() -> {
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
- notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
+ visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(
this, mNotifListContainer, mOnSettingsClickListener);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0cd82f0..562d3a5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,7 +29,6 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
-
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -39,16 +38,11 @@
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
-import static com.android.systemui.flags.Flags.FP_LISTEN_OCCLUDING_APPS;
-import static com.android.systemui.flags.Flags.STOP_FACE_AUTH_ON_DISPLAY_OFF;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
-
import static com.google.common.truth.Truth.assertThat;
-
import static junit.framework.Assert.assertEquals;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -145,7 +139,6 @@
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -299,7 +292,6 @@
private final Executor mBackgroundExecutor = Runnable::run;
private final Executor mMainExecutor = Runnable::run;
private TestableLooper mTestableLooper;
- private FakeFeatureFlags mFeatureFlags;
private Handler mHandler;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private MockitoSession mMockitoSession;
@@ -354,9 +346,6 @@
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false);
- mFeatureFlags.set(STOP_FACE_AUTH_ON_DISPLAY_OFF, false);
when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI);
@@ -1593,8 +1582,6 @@
@Test
public void listenForFingerprint_whenOccludingAppPkgOnAllowlist()
throws RemoteException {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, true);
-
// GIVEN keyguard isn't visible (app occluding)
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
@@ -1616,8 +1603,6 @@
@Test
public void doNotListenForFingerprint_whenOccludingAppPkgNotOnAllowlist()
throws RemoteException {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, true);
-
// GIVEN keyguard isn't visible (app occluding)
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
@@ -2979,11 +2964,6 @@
}
@Test
- public void stopFaceAuthOnDisplayOffFlagNotEnabled_doNotRegisterForDisplayCallback() {
- assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(0);
- }
-
- @Test
public void onDisplayOn_nothingHappens() throws RemoteException {
// GIVEN
keyguardIsVisible();
@@ -3325,7 +3305,6 @@
clearInvocations(mFingerprintManager);
clearInvocations(mBiometricManager);
clearInvocations(mStatusBarStateController);
- mFeatureFlags.set(STOP_FACE_AUTH_ON_DISPLAY_OFF, true);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
setupBiometrics(mKeyguardUpdateMonitor);
assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
@@ -3407,7 +3386,7 @@
mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
- Optional.of(mInteractiveToAuthProvider), mFeatureFlags,
+ Optional.of(mInteractiveToAuthProvider),
mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 0fb0b03..39fe6fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -751,6 +751,245 @@
}
@Test
+ public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ final int startingWidth = (int) (windowBounds.width() * 0.8);
+ final int startingHeight = (int) (windowBounds.height() * 0.8);
+ final float changeWindowSizeAmount = mContext.getResources().getFraction(
+ R.fraction.magnification_resize_window_size_amount,
+ /* base= */ 1,
+ /* pbase= */ 1);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mirrorView.performAccessibilityAction(
+ R.id.accessibility_action_increase_window_width, null);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // Window width includes the magnifier frame and the margin. Increasing the window size
+ // will be increasing the amount of the frame size only.
+ int newWindowWidth =
+ (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ + 2 * mirrorSurfaceMargin;
+ assertEquals(newWindowWidth, actualWindowWidth.get());
+ assertEquals(startingHeight, actualWindowHeight.get());
+ }
+
+ @Test
+ public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ final int startingWidth = (int) (windowBounds.width() * 0.8);
+ final int startingHeight = (int) (windowBounds.height() * 0.8);
+ final float changeWindowSizeAmount = mContext.getResources().getFraction(
+ R.fraction.magnification_resize_window_size_amount,
+ /* base= */ 1,
+ /* pbase= */ 1);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mirrorView.performAccessibilityAction(
+ R.id.accessibility_action_increase_window_height, null);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // Window height includes the magnifier frame and the margin. Increasing the window size
+ // will be increasing the amount of the frame size only.
+ int newWindowHeight =
+ (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ + 2 * mirrorSurfaceMargin;
+ assertEquals(startingWidth, actualWindowWidth.get());
+ assertEquals(newWindowHeight, actualWindowHeight.get());
+ }
+
+ @Test
+ public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ final int startingWidth = windowBounds.width();
+ final int startingHeight = windowBounds.height();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AccessibilityNodeInfo accessibilityNodeInfo =
+ mirrorView.createAccessibilityNodeInfo();
+ assertFalse(accessibilityNodeInfo.getActionList().contains(
+ new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
+ }
+
+ @Test
+ public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ final int startingWidth = windowBounds.width();
+ final int startingHeight = windowBounds.height();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AccessibilityNodeInfo accessibilityNodeInfo =
+ mirrorView.createAccessibilityNodeInfo();
+ assertFalse(accessibilityNodeInfo.getActionList().contains(
+ new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
+ }
+
+ @Test
+ public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() {
+ int mMinWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int startingSize = (int) (mMinWindowSize * 1.1);
+ final float changeWindowSizeAmount = mContext.getResources().getFraction(
+ R.fraction.magnification_resize_window_size_amount,
+ /* base= */ 1,
+ /* pbase= */ 1);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mirrorView.performAccessibilityAction(
+ R.id.accessibility_action_decrease_window_width, null);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // Window width includes the magnifier frame and the margin. Decreasing the window size
+ // will be decreasing the amount of the frame size only.
+ int newWindowWidth =
+ (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ + 2 * mirrorSurfaceMargin;
+ assertEquals(newWindowWidth, actualWindowWidth.get());
+ assertEquals(startingSize, actualWindowHeight.get());
+ }
+
+ @Test
+ public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() {
+ int mMinWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int startingSize = (int) (mMinWindowSize * 1.1);
+ final float changeWindowSizeAmount = mContext.getResources().getFraction(
+ R.fraction.magnification_resize_window_size_amount,
+ /* base= */ 1,
+ /* pbase= */ 1);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AtomicInteger actualWindowHeight = new AtomicInteger();
+ final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mirrorView.performAccessibilityAction(
+ R.id.accessibility_action_decrease_window_height, null);
+ actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+ actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ });
+
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // Window height includes the magnifier frame and the margin. Decreasing the window size
+ // will be decreasing the amount of the frame size only.
+ int newWindowHeight =
+ (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ + 2 * mirrorSurfaceMargin;
+ assertEquals(startingSize, actualWindowWidth.get());
+ assertEquals(newWindowHeight, actualWindowHeight.get());
+ }
+
+ @Test
+ public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() {
+ int mMinWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int startingSize = mMinWindowSize;
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AccessibilityNodeInfo accessibilityNodeInfo =
+ mirrorView.createAccessibilityNodeInfo();
+ assertFalse(accessibilityNodeInfo.getActionList().contains(
+ new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
+ }
+
+ @Test
+ public void windowHeightIsMin_noDecreaseWindowHeightA11yAcyion() {
+ int mMinWindowSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ final int startingSize = mMinWindowSize;
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+ mWindowMagnificationController.setEditMagnifierSizeMode(true);
+ });
+
+ final View mirrorView = mWindowManager.getAttachedView();
+ final AccessibilityNodeInfo accessibilityNodeInfo =
+ mirrorView.createAccessibilityNodeInfo();
+ assertFalse(accessibilityNodeInfo.getActionList().contains(
+ new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
+ }
+
+ @Test
public void enableWindowMagnification_hasA11yWindowTitle() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
@@ -1210,4 +1449,5 @@
when(mContext.getDisplay()).thenReturn(display);
return newRotation;
}
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 9751fad..2bb3785 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -28,6 +28,8 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
@@ -42,8 +44,6 @@
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -56,12 +56,15 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private val testDispatcher = utils.testDispatcher
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val featureFlags = FakeFeatureFlags()
private val keyguardRepository = FakeKeyguardRepository()
private val shadeRepository = FakeShadeRepository()
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+ private val sceneInteractor = utils.sceneInteractor()
private val userSetupRepository = FakeUserSetupRepository()
private val userRepository = FakeUserRepository()
private val configurationRepository = FakeConfigurationRepository()
@@ -126,6 +129,8 @@
ShadeInteractor(
testScope.backgroundScope,
disableFlagsRepository,
+ sceneContainerFlags,
+ { sceneInteractor },
keyguardRepository,
userSetupRepository,
deviceProvisionedController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index 7de78a6..469f65a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
@@ -44,6 +45,7 @@
@Mock lateinit var udfpsBpView: UdfpsBpView
@Mock lateinit var statusBarStateController: StatusBarStateController
@Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock lateinit var systemUIDialogManager: SystemUIDialogManager
@Mock lateinit var dumpManager: DumpManager
@@ -55,12 +57,13 @@
UdfpsBpViewController(
udfpsBpView,
statusBarStateController,
- shadeExpansionStateManager,
+ primaryBouncerInteractor,
systemUIDialogManager,
dumpManager
)
}
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
@Test
fun testShouldNeverPauseAuth() {
assertFalse(udfpsBpViewController.shouldPauseAuth())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 0e0d0e3..6eb637b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -50,7 +50,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -97,7 +96,6 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dialogManager: SystemUIDialogManager
@@ -145,13 +143,32 @@
block: () -> Unit
) {
controllerOverlay = UdfpsControllerOverlay(
- context, fingerprintManager, inflater, windowManager, accessibilityManager,
- statusBarStateController, shadeExpansionStateManager, statusBarKeyguardViewManager,
- keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
- configurationController, keyguardStateController, unlockedScreenOffAnimationController,
- udfpsDisplayMode, secureSettings, REQUEST_ID, reason,
- controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
- primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils,
+ context,
+ fingerprintManager,
+ inflater,
+ windowManager,
+ accessibilityManager,
+ statusBarStateController,
+ statusBarKeyguardViewManager,
+ keyguardUpdateMonitor,
+ dialogManager,
+ dumpManager,
+ transitionController,
+ configurationController,
+ keyguardStateController,
+ unlockedScreenOffAnimationController,
+ udfpsDisplayMode,
+ secureSettings,
+ REQUEST_ID,
+ reason,
+ controllerCallback,
+ onTouch,
+ activityLaunchAnimator,
+ featureFlags,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ isDebuggable,
+ udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e01b5af..755977f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -20,15 +20,12 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-
import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
-
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -98,7 +95,6 @@
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -314,19 +310,48 @@
(Provider<AlternateUdfpsTouchProvider>) () -> mAlternateTouchProvider)
: Optional.empty();
- mUdfpsController = new UdfpsController(mContext, new FakeExecution(), mLayoutInflater,
- mFingerprintManager, mWindowManager, mStatusBarStateController, mFgExecutor,
- new ShadeExpansionStateManager(), mStatusBarKeyguardViewManager, mDumpManager,
- mKeyguardUpdateMonitor, mFeatureFlags, mFalsingManager, mPowerManager,
- mAccessibilityManager, mLockscreenShadeTransitionController, mScreenLifecycle,
- mVibrator, mUdfpsHapticsSimulator, mUdfpsShell, mKeyguardStateController,
- mDisplayManager, mHandler, mConfigurationController, mSystemClock,
- mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
- mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor,
- mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
- mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils,
+ mUdfpsController = new UdfpsController(
+ mContext,
+ new FakeExecution(),
+ mLayoutInflater,
+ mFingerprintManager,
+ mWindowManager,
+ mStatusBarStateController,
+ mFgExecutor,
+ mStatusBarKeyguardViewManager,
+ mDumpManager,
+ mKeyguardUpdateMonitor,
+ mFeatureFlags,
+ mFalsingManager,
+ mPowerManager,
+ mAccessibilityManager,
+ mLockscreenShadeTransitionController,
+ mScreenLifecycle,
+ mVibrator,
+ mUdfpsHapticsSimulator,
+ mUdfpsShell,
+ mKeyguardStateController,
+ mDisplayManager,
+ mHandler,
+ mConfigurationController,
+ mSystemClock,
+ mUnlockedScreenOffAnimationController,
+ mSystemUIDialogManager,
+ mLatencyTracker,
+ mActivityLaunchAnimator,
+ alternateTouchProvider,
+ mBiometricExecutor,
+ mPrimaryBouncerInteractor,
+ mSinglePointerTouchProcessor,
+ mSessionTracker,
+ mAlternateBouncerInteractor,
+ mSecureSettings,
+ mInputManager,
+ mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
- mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels);
+ mUdfpsKeyguardAccessibilityDelegate,
+ mUdfpsKeyguardViewModels
+ );
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 032753a..3276e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -18,7 +18,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,15 +26,14 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -51,8 +49,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
// Dependencies
protected @Mock UdfpsKeyguardViewLegacy mView;
@@ -83,9 +79,6 @@
private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
protected StatusBarStateController.StateListener mStatusBarStateListener;
- private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
- protected List<ShadeExpansionListener> mExpansionListeners;
-
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallbackCaptor;
protected KeyguardStateController.Callback mKeyguardStateControllerCallback;
@@ -116,23 +109,6 @@
mStatusBarStateListener = mStateListenerCaptor.getValue();
}
- protected void captureStatusBarExpansionListeners() {
- verify(mShadeExpansionStateManager, times(2))
- .addExpansionListener(mExpansionListenerCaptor.capture());
- // first (index=0) is from super class, UdfpsAnimationViewController.
- // second (index=1) is from UdfpsKeyguardViewController
- mExpansionListeners = mExpansionListenerCaptor.getAllValues();
- }
-
- protected void updateStatusBarExpansion(float fraction, boolean expanded) {
- ShadeExpansionChangeEvent event =
- new ShadeExpansionChangeEvent(
- fraction, expanded, /* tracking= */ false, /* dragDownPxAmount= */ 0f);
- for (ShadeExpansionListener listener : mExpansionListeners) {
- listener.onPanelExpansionChanged(event);
- }
- }
-
protected void captureKeyguardStateControllerCallback() {
verify(mKeyguardStateController).addCallback(
mKeyguardStateControllerCallbackCaptor.capture());
@@ -155,7 +131,6 @@
UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
mView,
mStatusBarStateController,
- mShadeExpansionStateManager,
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
mDumpManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index d24290f..8508f45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
@@ -21,9 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,7 +32,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.RoboPilotTest;
-import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Test;
@@ -66,12 +63,6 @@
}
@Test
- public void testRegistersExpansionChangedListenerOnAttached() {
- mController.onViewAttached();
- captureStatusBarExpansionListeners();
- }
-
- @Test
public void testRegistersStatusBarStateListenersOnAttached() {
mController.onViewAttached();
captureStatusBarStateListeners();
@@ -98,14 +89,10 @@
public void testListenersUnregisteredOnDetached() {
mController.onViewAttached();
captureStatusBarStateListeners();
- captureStatusBarExpansionListeners();
captureKeyguardStateControllerCallback();
mController.onViewDetached();
verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
- for (ShadeExpansionListener listener : mExpansionListeners) {
- verify(mShadeExpansionStateManager).removeExpansionListener(listener);
- }
verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
}
@@ -134,23 +121,6 @@
}
@Test
- public void testFadeFromDialogSuggestedAlpha() {
- // GIVEN view is attached and status bar expansion is 1f
- mController.onViewAttached();
- captureStatusBarStateListeners();
- captureStatusBarExpansionListeners();
- updateStatusBarExpansion(1f, true);
- reset(mView);
-
- // WHEN dialog suggested alpha is .6f
- when(mView.getDialogSuggestedAlpha()).thenReturn(.6f);
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- // THEN alpha is updated based on dialog suggested alpha
- verify(mView).setUnpausedAlpha((int) (.6f * 255));
- }
-
- @Test
public void testShouldNotPauseAuthOnKeyguard() {
mController.onViewAttached();
captureStatusBarStateListeners();
@@ -250,72 +220,6 @@
}
@Test
- public void testFadeInWithStatusBarExpansion() {
- // GIVEN view is attached
- mController.onViewAttached();
- captureStatusBarExpansionListeners();
- captureKeyguardStateControllerCallback();
- reset(mView);
-
- // WHEN status bar expansion is 0
- updateStatusBarExpansion(0, true);
-
- // THEN alpha is 0
- verify(mView).setUnpausedAlpha(0);
- }
-
- @Test
- public void testTransitionToFullShadeProgress() {
- // GIVEN view is attached and status bar expansion is 1f
- mController.onViewAttached();
- captureStatusBarExpansionListeners();
- updateStatusBarExpansion(1f, true);
- reset(mView);
- when(mView.getDialogSuggestedAlpha()).thenReturn(1f);
-
- // WHEN we're transitioning to the full shade
- float transitionProgress = .6f;
- mController.setTransitionToFullShadeProgress(transitionProgress);
-
- // THEN alpha is between 0 and 255
- verify(mView).setUnpausedAlpha((int) ((1f - transitionProgress) * 255));
- }
-
- @Test
- public void testUpdatePanelExpansion_pauseAuth() {
- // GIVEN view is attached + on the keyguard
- mController.onViewAttached();
- captureStatusBarStateListeners();
- captureStatusBarExpansionListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
- reset(mView);
-
- // WHEN panelViewExpansion changes to hide
- when(mView.getUnpausedAlpha()).thenReturn(0);
- updateStatusBarExpansion(0f, false);
-
- // THEN pause auth is updated to PAUSE
- verify(mView, atLeastOnce()).setPauseAuth(true);
- }
-
- @Test
- public void testUpdatePanelExpansion_unpauseAuth() {
- // GIVEN view is attached + on the keyguard + panel expansion is 0f
- mController.onViewAttached();
- captureStatusBarStateListeners();
- captureStatusBarExpansionListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
- reset(mView);
-
- // WHEN panelViewExpansion changes to expanded
- when(mView.getUnpausedAlpha()).thenReturn(255);
- updateStatusBarExpansion(1f, true);
-
- // THEN pause auth is updated to NOT pause
- verify(mView, atLeastOnce()).setPauseAuth(false);
- }
-
- @Test
// TODO(b/259264861): Tracking Bug
public void testUdfpsExpandedOverlayOn() {
// GIVEN view is attached and useExpandedOverlay is true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 8dfeb3b..1885f64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -39,8 +39,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -51,6 +53,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -62,15 +65,15 @@
@kotlinx.coroutines.ExperimentalCoroutinesApi
class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
UdfpsKeyguardViewLegacyControllerBaseTest() {
- lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
- @Mock private lateinit var bouncerLogger: TableLogBuffer
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
- private lateinit var testScope: TestScope
+ private lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
+
+ @Mock private lateinit var bouncerLogger: TableLogBuffer
@Before
override fun setUp() {
- testScope = TestScope()
-
allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
MockitoAnnotations.initMocks(this)
keyguardBouncerRepository =
@@ -82,7 +85,7 @@
super.setUp()
}
- override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy? {
+ override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy {
mPrimaryBouncerInteractor =
PrimaryBouncerInteractor(
keyguardBouncerRepository,
@@ -115,6 +118,70 @@
}
@Test
+ fun bouncerExpansionChange_fadeIn() =
+ testScope.runTest {
+ // GIVEN view is attached
+ mController.onViewAttached()
+ captureKeyguardStateControllerCallback()
+ Mockito.reset(mView)
+
+ // WHEN status bar expansion is 0
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+ runCurrent()
+
+ // THEN alpha is 0
+ verify(mView).unpausedAlpha = 0
+
+ job.cancel()
+ }
+
+ @Test
+ fun bouncerExpansionChange_pauseAuth() =
+ testScope.runTest {
+ // GIVEN view is attached + on the keyguard
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+ Mockito.reset(mView)
+
+ // WHEN panelViewExpansion changes to hide
+ whenever(mView.unpausedAlpha).thenReturn(0)
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+ runCurrent()
+
+ // THEN pause auth is updated to PAUSE
+ verify(mView, Mockito.atLeastOnce()).setPauseAuth(true)
+
+ job.cancel()
+ }
+
+ @Test
+ fun bouncerExpansionChange_unpauseAuth() =
+ testScope.runTest {
+ // GIVEN view is attached + on the keyguard + panel expansion is 0f
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+ Mockito.reset(mView)
+
+ // WHEN panelViewExpansion changes to expanded
+ whenever(mView.unpausedAlpha).thenReturn(255)
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
+ runCurrent()
+
+ // THEN pause auth is updated to NOT pause
+ verify(mView, Mockito.atLeastOnce()).setPauseAuth(false)
+
+ job.cancel()
+ }
+
+ @Test
fun shadeLocked_showAlternateBouncer_unpauseAuth() =
testScope.runTest {
// GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing)
@@ -154,4 +221,48 @@
job.cancel()
}
+
+ @Test
+ fun fadeFromDialogSuggestedAlpha() =
+ testScope.runTest {
+ // GIVEN view is attached and status bar expansion is 1f
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
+ runCurrent()
+ Mockito.reset(mView)
+
+ // WHEN dialog suggested alpha is .6f
+ whenever(mView.dialogSuggestedAlpha).thenReturn(.6f)
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+
+ // THEN alpha is updated based on dialog suggested alpha
+ verify(mView).unpausedAlpha = (.6f * 255).toInt()
+
+ job.cancel()
+ }
+
+ @Test
+ fun transitionToFullShadeProgress() =
+ testScope.runTest {
+ // GIVEN view is attached and status bar expansion is 1f
+ mController.onViewAttached()
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
+ runCurrent()
+ Mockito.reset(mView)
+ whenever(mView.dialogSuggestedAlpha).thenReturn(1f)
+
+ // WHEN we're transitioning to the full shade
+ val transitionProgress = .6f
+ mController.setTransitionToFullShadeProgress(transitionProgress)
+
+ // THEN alpha is between 0 and 255
+ verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt()
+
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 90076fd..ffcaeee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.dreams.touch;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -295,7 +294,8 @@
}
/**
- * Makes sure the expansion amount is proportional to (1 - scroll).
+ * Verifies that swiping up when the lock pattern is not secure does not consume the scroll
+ * gesture or expand.
*/
@Test
public void testSwipeUp_keyguardNotSecure_doesNotExpand() {
@@ -314,8 +314,10 @@
0, SCREEN_HEIGHT_PX - distanceY, 0);
reset(mScrimController);
+
+ // Scroll gesture is not consumed.
assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
- .isTrue();
+ .isFalse();
// We should not expand since the keyguard is not secure
verify(mScrimController, never()).expand(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index bdcb9ab..b81a3d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -159,7 +159,6 @@
screenOffAnimationController = mock(),
statusBarStateController = mock(),
),
- FakeFeatureFlags().apply { set(Flags.FP_LISTEN_OCCLUDING_APPS, true) },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 77b3e69f..9386d71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -17,8 +17,9 @@
package com.android.systemui.qs.pipeline.data.repository
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -34,7 +35,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class AutoAddSettingsRepositoryTest : SysuiTestCase() {
private val secureSettings = FakeSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
index d7ab903..30f5811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -18,8 +18,9 @@
import android.content.ComponentName
import android.content.SharedPreferences
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
@@ -29,7 +30,8 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
private lateinit var underTest: CustomTileAddedSharedPrefsRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index dc0fae5..995de66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -29,8 +29,10 @@
import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.service.quicksettings.TileService
-import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.mockito.any
@@ -60,7 +62,9 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 72c31b1..aef5faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -17,9 +17,10 @@
package com.android.systemui.qs.pipeline.data.repository
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.QSHost
@@ -41,7 +42,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class TileSpecSettingsRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
index 817ac61..6e579d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
@@ -17,9 +17,10 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.util.mockito.mock
@@ -28,7 +29,8 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class AutoAddableSettingListTest : SysuiTestCase() {
private val factory =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index 36c3c9d..7c6dd24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -35,7 +36,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class AutoAddableSettingTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
index afb43c7..469eee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -35,7 +36,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class CallbackControllerAutoAddableTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index a357dad..b6eaa39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +42,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class CastAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var castController: CastController
@@ -128,6 +130,6 @@
}
companion object {
- private val SPEC = TileSpec.create(CastTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(CastTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
index 098ffc3..a755fbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -40,7 +41,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class DataSaverAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var dataSaverController: DataSaverController
@@ -80,6 +82,6 @@
}
companion object {
- private val SPEC = TileSpec.create(DataSaverTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(DataSaverTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
index a2e3538..daacca51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -43,7 +44,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class DeviceControlsAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var deviceControlsController: DeviceControlsController
@@ -110,6 +112,6 @@
}
companion object {
- private val SPEC = TileSpec.create(DeviceControlsTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(DeviceControlsTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
index ee96b47..4b5f7f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -40,7 +41,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class HotspotAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var hotspotController: HotspotController
@@ -78,6 +80,6 @@
}
companion object {
- private val SPEC = TileSpec.create(HotspotTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(HotspotTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
index e03072a..32d9db2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
@@ -17,8 +17,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.hardware.display.NightDisplayListener
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
@@ -49,7 +50,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class NightDisplayAutoAddableTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_SELF)
@@ -121,6 +123,6 @@
}
companion object {
- private val SPEC = TileSpec.create(NightDisplayTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(NightDisplayTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
index 7b4a55e..fb513a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.ReduceBrightColorsController
@@ -43,7 +44,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var reduceBrightColorsController: ReduceBrightColorsController
@@ -103,6 +105,6 @@
}
companion object {
- private val SPEC = TileSpec.create(ReduceBrightColorsTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(ReduceBrightColorsTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
index fb35a3a..8036cb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
@@ -18,9 +18,10 @@
import android.content.ComponentName
import android.content.pm.PackageManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -51,7 +52,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class SafetyCenterAutoAddableTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -155,9 +157,10 @@
companion object {
private const val SAFETY_TILE_CLASS_NAME = "cls"
private const val PERMISSION_CONTROLLER_PACKAGE_NAME = "pkg"
- private val SPEC =
+ private val SPEC by lazy {
TileSpec.create(
ComponentName(PERMISSION_CONTROLLER_PACKAGE_NAME, SAFETY_TILE_CLASS_NAME)
)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
index 6b250f4..1c8cb54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -37,7 +38,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class WalletAutoAddableTest : SysuiTestCase() {
@Mock private lateinit var walletController: WalletController
@@ -76,6 +78,6 @@
}
companion object {
- private val SPEC = TileSpec.create(QuickAccessWalletTile.TILE_SPEC)
+ private val SPEC by lazy { TileSpec.create(QuickAccessWalletTile.TILE_SPEC) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index e9f7c8ab..de1d29fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -21,8 +21,9 @@
import android.content.pm.UserInfo.FLAG_MANAGED_PROFILE
import android.content.pm.UserInfo.FLAG_PRIMARY
import android.content.pm.UserInfo.FLAG_PROFILE
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -40,7 +41,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
private lateinit var userTracker: FakeUserTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index f924b35..bb18115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.pipeline.domain.interactor
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
@@ -46,7 +47,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class AutoAddInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 54a9360..dc1b9c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -22,8 +22,9 @@
import android.content.pm.UserInfo
import android.os.UserHandle
import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.nano.SystemUIProtoDump
@@ -69,7 +70,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class CurrentTilesInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
index 6556cfd..151b256 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
@@ -15,8 +15,9 @@
*/
package com.android.systemui.qs.pipeline.domain.interactor
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeController
import org.junit.Before
@@ -26,7 +27,8 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@SmallTest
class PanelInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
index d880172..34c4c98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
@@ -17,15 +17,17 @@
package com.android.systemui.qs.pipeline.shared
import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
class TileSpecTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 1e47f78..0d694ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -51,30 +51,6 @@
/** Tests the Java-compatible function wrapper, ensures callback is invoked. */
@Test
- fun testProcessAsync() {
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_KEY_OTHER)
- .setBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
- .build()
- val processor = RequestProcessor(imageCapture, policy, flags, scope)
-
- var result: ScreenshotRequest? = null
- var callbackCount = 0
- val callback: (ScreenshotRequest) -> Unit = { processedRequest: ScreenshotRequest ->
- result = processedRequest
- callbackCount++
- }
-
- // runs synchronously, using Unconfined Dispatcher
- processor.processAsync(request, callback)
-
- // Callback invoked once returning the same request (no changes)
- assertThat(callbackCount).isEqualTo(1)
- assertThat(result).isEqualTo(request)
- }
-
- /** Tests the Java-compatible function wrapper, ensures callback is invoked. */
- @Test
fun testProcessAsync_ScreenshotData() {
val request =
ScreenshotData.fromRequest(
@@ -112,13 +88,6 @@
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
- val processedRequest = processor.process(request)
-
- // Request has topComponent added, but otherwise unchanged.
- assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
- assertThat(processedRequest.source).isEqualTo(SCREENSHOT_OTHER)
- assertThat(processedRequest.topComponent).isEqualTo(component)
-
val processedData = processor.process(ScreenshotData.fromRequest(request))
// Request has topComponent added, but otherwise unchanged.
@@ -144,18 +113,6 @@
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
- val processedRequest = processor.process(request)
-
- // Expect a task snapshot is taken, overriding the full screen mode
- assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
- assertThat(bitmap.equalsHardwareBitmap(processedRequest.bitmap)).isTrue()
- assertThat(processedRequest.boundsInScreen).isEqualTo(bounds)
- assertThat(processedRequest.insets).isEqualTo(Insets.NONE)
- assertThat(processedRequest.taskId).isEqualTo(TASK_ID)
- assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
- assertThat(processedRequest.userId).isEqualTo(USER_ID)
- assertThat(processedRequest.topComponent).isEqualTo(component)
-
val processedData = processor.process(ScreenshotData.fromRequest(request))
// Expect a task snapshot is taken, overriding the full screen mode
@@ -165,8 +122,6 @@
assertThat(processedData.insets).isEqualTo(Insets.NONE)
assertThat(processedData.taskId).isEqualTo(TASK_ID)
assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
- assertThat(processedRequest.userId).isEqualTo(USER_ID)
- assertThat(processedRequest.topComponent).isEqualTo(component)
}
@Test
@@ -186,9 +141,6 @@
val processor = RequestProcessor(imageCapture, policy, flags, scope)
Assert.assertThrows(IllegalStateException::class.java) {
- runBlocking { processor.process(request) }
- }
- Assert.assertThrows(IllegalStateException::class.java) {
runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
}
}
@@ -212,11 +164,6 @@
.setInsets(Insets.NONE)
.build()
- val processedRequest = processor.process(request)
-
- // No changes
- assertThat(processedRequest).isEqualTo(request)
-
val screenshotData = ScreenshotData.fromRequest(request)
val processedData = processor.process(screenshotData)
@@ -243,14 +190,10 @@
.setInsets(Insets.NONE)
.build()
- val processedRequest = processor.process(request)
-
- // Work profile, but already a task snapshot, so no changes
- assertThat(processedRequest).isEqualTo(request)
-
val screenshotData = ScreenshotData.fromRequest(request)
val processedData = processor.process(screenshotData)
+ // Work profile, but already a task snapshot, so no changes
assertThat(processedData).isEqualTo(screenshotData)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 97c2ed4..cfdf66e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.screenshot
import android.content.ComponentName
+import android.graphics.Bitmap
import android.net.Uri
import android.testing.AndroidTestingRunner
import android.view.Display
@@ -10,6 +11,7 @@
import android.view.Display.TYPE_VIRTUAL
import android.view.Display.TYPE_WIFI
import android.view.WindowManager
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotRequest
@@ -95,6 +97,35 @@
}
@Test
+ fun executeScreenshots_providedImageType_callsOnlyDefaultDisplayController() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(
+ createScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE),
+ onSaved,
+ callback
+ )
+
+ verify(controllerFactory).create(eq(0))
+ verify(controllerFactory, never()).create(eq(1))
+
+ val capturer = ArgumentCaptor<ScreenshotData>()
+
+ verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ assertThat(capturer.value.displayId).isEqualTo(0)
+ // OnSaved callback should be different.
+ verify(controller1, never()).handleScreenshot(any(), any(), any())
+
+ assertThat(eventLogger.numLogs()).isEqualTo(1)
+ assertThat(eventLogger.get(0).eventId)
+ .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
+ assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
fun executeScreenshots_onlyVirtualDisplays_noInteractionsWithControllers() =
testScope.runTest {
setDisplays(display(TYPE_VIRTUAL, id = 0), display(TYPE_VIRTUAL, id = 1))
@@ -283,12 +314,14 @@
runCurrent()
}
- private fun createScreenshotRequest() =
- ScreenshotRequest.Builder(
- WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
- WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
- )
+ private fun createScreenshotRequest(type: Int = WindowManager.TAKE_SCREENSHOT_FULLSCREEN) =
+ ScreenshotRequest.Builder(type, WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER)
.setTopComponent(topComponent)
+ .also {
+ if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ it.setBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))
+ }
+ }
.build()
private class FakeRequestProcessor : ScreenshotRequestProcessor {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index a08cda6..6205d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -20,10 +20,6 @@
import android.app.admin.DevicePolicyResources.Strings.SystemUi.SCREENSHOT_BLOCKED_BY_ADMIN
import android.app.admin.DevicePolicyResourcesManager
import android.content.ComponentName
-import android.graphics.Bitmap
-import android.graphics.Bitmap.Config.HARDWARE
-import android.graphics.ColorSpace
-import android.hardware.HardwareBuffer
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
@@ -94,14 +90,6 @@
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
- val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
- val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
- consumer.accept(request)
- }
- .whenever(requestProcessor)
- .processAsync(/* request= */ any(ScreenshotRequest::class.java), /* callback= */ any())
-
- doAnswer {
val request: ScreenshotData = it.getArgument(0) as ScreenshotData
val consumer: Consumer<ScreenshotData> = it.getArgument(1)
consumer.accept(request)
@@ -353,23 +341,3 @@
return service
}
}
-
-private fun Bitmap.equalsHardwareBitmap(other: Bitmap): Boolean {
- return config == HARDWARE &&
- other.config == HARDWARE &&
- hardwareBuffer == other.hardwareBuffer &&
- colorSpace == other.colorSpace
-}
-
-/** A hardware Bitmap is mandated by use of ScreenshotHelper.HardwareBitmapBundler */
-private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
- val buffer =
- HardwareBuffer.create(
- width,
- height,
- HardwareBuffer.RGBA_8888,
- 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
- )
- return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index e42a7a6..46b636b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -34,7 +34,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.TestScopeProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
@@ -48,6 +47,8 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -99,7 +100,8 @@
protected QuickSettingsController mQsController;
- protected TestScope mTestScope = TestScopeProvider.getTestScope();
+ protected SceneTestUtils mUtils = new SceneTestUtils(this);
+ protected TestScope mTestScope = mUtils.getTestScope();
@Mock
protected Resources mResources;
@@ -172,6 +174,8 @@
new ShadeInteractor(
mTestScope.getBackgroundScope(),
mDisableFlagsRepository,
+ new FakeSceneContainerFlags(),
+ () -> mUtils.sceneInteractor(),
mKeyguardRepository,
new FakeUserSetupRepository(),
mDeviceProvisionedController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index ba5ecce..dd6ab73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -35,6 +35,10 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
@@ -52,9 +56,8 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -67,9 +70,11 @@
class ShadeInteractorTest : SysuiTestCase() {
private lateinit var underTest: ShadeInteractor
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
private val featureFlags = FakeFeatureFlags()
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+ private val sceneInteractor = utils.sceneInteractor()
private val userSetupRepository = FakeUserSetupRepository()
private val userRepository = FakeUserRepository()
private val disableFlagsRepository = FakeDisableFlagsRepository()
@@ -103,7 +108,7 @@
val refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
- mainDispatcher = testDispatcher,
+ mainDispatcher = utils.testDispatcher,
repository = userRepository,
)
@@ -141,7 +146,7 @@
),
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = testDispatcher,
+ backgroundDispatcher = utils.testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
@@ -151,6 +156,8 @@
ShadeInteractor(
testScope.backgroundScope,
disableFlagsRepository,
+ sceneContainerFlags,
+ { sceneInteractor },
keyguardRepository,
userSetupRepository,
deviceProvisionedController,
@@ -557,4 +564,146 @@
// THEN anyExpanding is false
assertThat(actual).isFalse()
}
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onScene() =
+ testScope.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on the scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onDifferentScene() =
+ testScope.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on a different scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toScene() =
+ testScope.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = key,
+ progress = progress,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion matches the progress
+ assertThat(expansionAmount).isEqualTo(.4f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 1
+ assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_fromScene() =
+ testScope.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to move to the scene
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = key,
+ toScene = SceneKey.Lockscreen,
+ progress = progress,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ assertThat(expansionAmount).isEqualTo(1f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion reflects the progress
+ assertThat(expansionAmount).isEqualTo(.6f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 0
+ assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
+ testScope.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially complete
+ progress.value = .4f
+
+ // THEN expansion is still 0
+ assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is still 0
+ assertThat(expansionAmount).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 6f75880..2446234 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -20,6 +20,8 @@
import com.android.systemui.plugins.qs.QS
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -39,8 +41,6 @@
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Assert.assertFalse
@@ -74,8 +74,8 @@
@RunWith(AndroidTestingRunner::class)
@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
-
- private val testScope = TestScope(StandardTestDispatcher())
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@@ -102,6 +102,8 @@
@Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+ private val sceneInteractor = utils.sceneInteractor()
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
private val configurationRepository = FakeConfigurationRepository()
@@ -113,6 +115,8 @@
ShadeInteractor(
testScope.backgroundScope,
disableFlagsRepository,
+ sceneContainerFlags,
+ { sceneInteractor },
keyguardRepository,
userSetupRepository = FakeUserSetupRepository(),
deviceProvisionedController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6f431be..79cf932 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -628,6 +628,16 @@
verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
}
+ @Test
+ public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
+ initController(/* viewIsAttached= */ true);
+ mController.onKeyguardTransitionChanged(
+ new TransitionStep(
+ /* from= */ KeyguardState.OCCLUDED,
+ /* to= */ KeyguardState.AOD));
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 75fb22d..521069f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -33,6 +33,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
@@ -46,8 +48,6 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -59,12 +59,16 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
- private val testScope = TestScope(StandardTestDispatcher())
+ private val utils = SceneTestUtils(this)
+
+ private val testScope = utils.testScope
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val userSetupRepository = FakeUserSetupRepository()
private val shadeRepository = FakeShadeRepository()
private val keyguardRepository = FakeKeyguardRepository()
+ private val sceneContainerFlags = FakeSceneContainerFlags()
+ private val sceneInteractor = utils.sceneInteractor()
private lateinit var configurationRepository: FakeConfigurationRepository
private lateinit var sharedNotificationContainerInteractor:
@@ -107,6 +111,8 @@
ShadeInteractor(
testScope.backgroundScope,
disableFlagsRepository,
+ sceneContainerFlags,
+ { sceneInteractor },
keyguardRepository,
userSetupRepository,
deviceProvisionedController,
@@ -220,6 +226,14 @@
)
)
assertThat(isOnLockscreen).isTrue()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ assertThat(isOnLockscreen).isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 5107ecc..20e732a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,12 +16,9 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.FP_LISTEN_OCCLUDING_APPS;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -133,7 +130,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false);
mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
@@ -439,24 +435,6 @@
@Test
public void onFPFailureNoHaptics_notInteractive_showLockScreen() {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, true);
-
- // GIVEN no vibrator and device is not interactive
- when(mVibratorHelper.hasVibrator()).thenReturn(false);
- when(mUpdateMonitor.isDeviceInteractive()).thenReturn(false);
- when(mUpdateMonitor.isDreaming()).thenReturn(false);
-
- // WHEN FP fails
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
-
- // THEN wakeup the device
- verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
- public void onFPFailureNoHaptics_notInteractive_showLockScreen_doNotListenOccludingApps() {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false);
-
// GIVEN no vibrator and device is not interactive
when(mVibratorHelper.hasVibrator()).thenReturn(false);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(false);
@@ -471,8 +449,6 @@
@Test
public void onFPFailureNoHaptics_dreaming_showLockScreen() {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, true);
-
// GIVEN no vibrator and device is dreaming
when(mVibratorHelper.hasVibrator()).thenReturn(false);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
@@ -486,22 +462,6 @@
}
@Test
- public void onFPFailureNoHaptics_dreaming_showLockScreen_doNotListeOccludingApps() {
- mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false);
-
- // GIVEN no vibrator and device is dreaming
- when(mVibratorHelper.hasVibrator()).thenReturn(false);
- when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
- when(mUpdateMonitor.isDreaming()).thenReturn(true);
-
- // WHEN FP fails
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
-
- // THEN wakeup the device
- verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
public void onSideFingerprintSuccess_recentPowerButtonPress_noHaptic() {
// GIVEN side fingerprint enrolled, last wake reason was power button
when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 34c4ac1..233f407 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -54,8 +53,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -72,8 +71,8 @@
@RunWithLooper()
public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider =
- mock(NotificationInterruptStateProvider.class);
+ private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
+ mock(VisualInterruptionDecisionProvider.class);
private NotificationInterruptSuppressor mInterruptSuppressor;
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
@@ -125,14 +124,14 @@
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
mInitController,
- mNotificationInterruptStateProvider,
+ mVisualInterruptionDecisionProvider,
mock(NotificationRemoteInputManager.class),
mock(NotificationRemoteInputManager.Callback.class),
mock(NotificationListContainer.class));
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
- verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture());
+ verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
mInterruptSuppressor = suppressorCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 1b623a3..7595a54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -134,6 +134,7 @@
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleEducationController;
import com.android.wm.shell.bubbles.BubbleEntry;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubbleOverflow;
@@ -277,6 +278,8 @@
@Mock
private BubbleLogger mBubbleLogger;
@Mock
+ private BubbleEducationController mEducationController;
+ @Mock
private TaskStackListenerImpl mTaskStackListener;
@Mock
private KeyguardStateController mKeyguardStateController;
@@ -369,7 +372,8 @@
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
mPositioner.setMaxBubbles(5);
- mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
+ mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, mEducationController,
+ syncExecutor);
when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
Collections.singletonList(mock(UserInfo.class)));
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index 2865710..aa8dbe1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.pipeline.data.repository
-import android.util.Log
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.shared.TileSpec
import kotlinx.coroutines.flow.Flow
@@ -28,7 +27,7 @@
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
- return getFlow(userId).asStateFlow().also { Log.d("Fabian", "Retrieving flow for $userId") }
+ return getFlow(userId).asStateFlow()
}
override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 9dea0a0..2d79e0f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -127,6 +127,7 @@
)
}
+ @JvmOverloads
fun sceneInteractor(
repository: SceneContainerRepository = fakeSceneContainerRepository()
): SceneInteractor {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b941aaf..8e538b2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -192,7 +192,6 @@
"apache-commons-math",
"power_optimization_flags_lib",
"notification_flags_lib",
- "pm_flags_lib",
"camera_platform_flags_core_java_lib",
],
javac_shard_size: 50,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 87077a6..a451f36 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -159,7 +159,6 @@
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.am.PlatformCompatCache.CachedCompatChangeId;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -317,11 +316,6 @@
static final long USE_SHORT_FGS_USAGE_INTERACTION_TIME = 183972877L;
/**
- * For some direct access we need to power manager.
- */
- PowerManagerInternal mLocalPowerManager;
-
- /**
* Service for optimizing resource usage from background apps.
*/
CachedAppOptimizer mCachedAppOptimizer;
@@ -431,7 +425,6 @@
mProcLock = service.mProcLock;
mActiveUids = activeUids;
- mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
mConstants = mService.mConstants;
mCachedAppOptimizer = new CachedAppOptimizer(mService);
mCacheOomRanker = new CacheOomRanker(service);
@@ -1480,8 +1473,8 @@
becameIdle.clear();
// Update from any uid changes.
- if (mLocalPowerManager != null) {
- mLocalPowerManager.startUidChanges();
+ if (mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.startUidChanges();
}
for (int i = activeUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = activeUids.valueAt(i);
@@ -1575,8 +1568,8 @@
}
mService.mInternal.deletePendingTopUid(uidRec.getUid(), nowElapsed);
}
- if (mLocalPowerManager != null) {
- mLocalPowerManager.finishUidChanges();
+ if (mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.finishUidChanges();
}
int size = becameIdle.size();
@@ -3613,8 +3606,8 @@
final long nowElapsed = SystemClock.elapsedRealtime();
final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
long nextTime = 0;
- if (mLocalPowerManager != null) {
- mLocalPowerManager.startUidChanges();
+ if (mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.startUidChanges();
}
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
@@ -3634,8 +3627,8 @@
}
}
}
- if (mLocalPowerManager != null) {
- mLocalPowerManager.finishUidChanges();
+ if (mService.mLocalPowerManager != null) {
+ mService.mLocalPowerManager.finishUidChanges();
}
// Also check if there are any apps in cached and background restricted mode,
// if so, kill it if it's been there long enough, or kick off a msg to check
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 4d8c02b..2c3c66e 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -7,4 +7,5 @@
namespace: "display_manager"
description: "Feature flag for Connected Display managment"
bug: "280739508"
+ is_fixed_read_only: true
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5b87069..e538355 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,7 +22,6 @@
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
-
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.media.MediaFeatureFlagManager.FEATURE_SCANNING_MINIMUM_PACKAGE_IMPORTANCE;
@@ -214,17 +213,16 @@
@NonNull
public List<MediaRoute2Info> getSystemRoutes() {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
- final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- == PackageManager.PERMISSION_GRANTED;
+ final boolean hasSystemRoutingPermission = checkCallerHasSystemRoutingPermissions(pid, uid);
final long token = Binder.clearCallingIdentity();
try {
Collection<MediaRoute2Info> systemRoutes;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- if (hasModifyAudioRoutingPermission) {
+ if (hasSystemRoutingPermission) {
MediaRoute2ProviderInfo providerInfo =
userRecord.mHandler.mSystemProvider.getProviderInfo();
if (providerInfo != null) {
@@ -255,9 +253,8 @@
final boolean hasConfigureWifiDisplayPermission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
== PackageManager.PERMISSION_GRANTED;
- final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- == PackageManager.PERMISSION_GRANTED;
+ final boolean hasModifyAudioRoutingPermission =
+ checkCallerHasModifyAudioRoutingPermission(pid, uid);
final long token = Binder.clearCallingIdentity();
try {
@@ -666,17 +663,17 @@
public RoutingSessionInfo getSystemSessionInfo(
@Nullable String packageName, boolean setDeviceRouteSelected) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
- final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- == PackageManager.PERMISSION_GRANTED;
+ final boolean hasSystemRoutingPermissions =
+ checkCallerHasSystemRoutingPermissions(pid, uid);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
List<RoutingSessionInfo> sessionInfos;
- if (hasModifyAudioRoutingPermission) {
+ if (hasSystemRoutingPermissions) {
if (setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
@@ -707,6 +704,25 @@
}
}
+ private boolean checkCallerHasSystemRoutingPermissions(int pid, int uid) {
+ return checkCallerHasModifyAudioRoutingPermission(pid, uid);
+ }
+
+ private boolean checkCallerHasModifyAudioRoutingPermission(int pid, int uid) {
+ return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean checkCallerHasBluetoothPermissions(int pid, int uid) {
+ boolean hasBluetoothRoutingPermission = true;
+ for (String permission : BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING) {
+ hasBluetoothRoutingPermission &=
+ mContext.checkPermission(permission, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return hasBluetoothRoutingPermission;
+ }
+
// End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -1587,20 +1603,11 @@
mPid = pid;
mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission;
mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission;
- mHasBluetoothRoutingPermission = new AtomicBoolean(fetchBluetoothPermission());
+ mHasBluetoothRoutingPermission =
+ new AtomicBoolean(checkCallerHasBluetoothPermissions(mPid, mUid));
mRouterId = mNextRouterOrManagerId.getAndIncrement();
}
- private boolean fetchBluetoothPermission() {
- boolean hasBluetoothRoutingPermission = true;
- for (String permission : BLUETOOTH_PERMISSIONS_FOR_SYSTEM_ROUTING) {
- hasBluetoothRoutingPermission &=
- mContext.checkPermission(permission, mPid, mUid)
- == PackageManager.PERMISSION_GRANTED;
- }
- return hasBluetoothRoutingPermission;
- }
-
/**
* Returns whether the corresponding router has permission to query and control system
* routes.
@@ -1611,7 +1618,7 @@
public void maybeUpdateSystemRoutingPermissionLocked() {
boolean oldSystemRoutingPermissionValue = hasSystemRoutingPermission();
- mHasBluetoothRoutingPermission.set(fetchBluetoothPermission());
+ mHasBluetoothRoutingPermission.set(checkCallerHasBluetoothPermissions(mPid, mUid));
boolean newSystemRoutingPermissionValue = hasSystemRoutingPermission();
if (oldSystemRoutingPermissionValue != newSystemRoutingPermissionValue) {
Map<String, MediaRoute2Info> routesToReport =
diff --git a/services/core/java/com/android/server/pm/Android.bp b/services/core/java/com/android/server/pm/Android.bp
deleted file mode 100644
index 89c0124..0000000
--- a/services/core/java/com/android/server/pm/Android.bp
+++ /dev/null
@@ -1,12 +0,0 @@
-aconfig_declarations {
- name: "pm_flags",
- package: "com.android.server.pm",
- srcs: [
- "*.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "pm_flags_lib",
- aconfig_declarations: "pm_flags",
-}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 69a6c13..ffa2af1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -58,6 +58,7 @@
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.HIDE_EPHEMERAL_APIS;
import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatureArrays;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell;
import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
@@ -4215,8 +4216,7 @@
if (p2SigningDetails == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- int result = compareSignatures(p1SigningDetails.getSignatures(),
- p2SigningDetails.getSignatures());
+ int result = compareSignatures(p1SigningDetails, p2SigningDetails);
if (result == PackageManager.SIGNATURE_MATCH) {
return result;
}
@@ -4231,7 +4231,7 @@
Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
: p2SigningDetails.getSignatures();
- result = compareSignatures(p1Signatures, p2Signatures);
+ result = compareSignatureArrays(p1Signatures, p2Signatures);
}
return result;
}
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 8f7b721..76203ac 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -30,7 +30,6 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageArchiverService;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstaller;
@@ -100,9 +99,6 @@
private final PackageInstallerService mInstallerService;
@NonNull
- private final PackageArchiverService mPackageArchiverService;
-
- @NonNull
private final PackageProperty mPackageProperty;
@NonNull
@@ -131,8 +127,7 @@
@Nullable ComponentName instantAppResolverSettingsComponent,
@NonNull String requiredSupplementalProcessPackage,
@Nullable String servicesExtensionPackageName,
- @Nullable String sharedSystemSharedLibraryPackageName,
- @NonNull PackageArchiverService packageArchiverService) {
+ @Nullable String sharedSystemSharedLibraryPackageName) {
mService = service;
mContext = context;
mDexOptHelper = dexOptHelper;
@@ -148,7 +143,6 @@
mRequiredSupplementalProcessPackage = requiredSupplementalProcessPackage;
mServicesExtensionPackageName = servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = sharedSystemSharedLibraryPackageName;
- mPackageArchiverService = packageArchiverService;
}
protected Computer snapshot() {
@@ -622,12 +616,6 @@
@Override
@Deprecated
- public final IPackageArchiverService getPackageArchiverService() {
- return mPackageArchiverService;
- }
-
- @Override
- @Deprecated
public final void getPackageSizeInfo(final String packageName, int userId,
final IPackageStatsObserver observer) {
throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d668146..468b3a7 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4728,8 +4728,7 @@
synchronized (mPm.mLock) {
platformPkgSetting = mPm.mSettings.getPackageLPr("android");
}
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
+ if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails())) {
throw PackageManagerException.ofInternalError("Overlay "
+ pkg.getPackageName()
+ " must target Q or later, "
@@ -4751,8 +4750,7 @@
targetPkgSetting = mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
}
if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
+ if (!comparePackageSignatures(targetPkgSetting, pkg.getSigningDetails())) {
// check reference signature
if (mPm.mOverlayConfigSignaturePackage == null) {
throw PackageManagerException.ofInternalError("Overlay "
@@ -4767,8 +4765,7 @@
refPkgSetting = mPm.mSettings.getPackageLPr(
mPm.mOverlayConfigSignaturePackage);
}
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
+ if (!comparePackageSignatures(refPkgSetting, pkg.getSigningDetails())) {
throw PackageManagerException.ofInternalError("Overlay "
+ pkg.getPackageName() + " signed with a different "
+ "certificate than both the reference package and "
@@ -4799,8 +4796,7 @@
synchronized (mPm.mLock) {
platformPkgSetting = mPm.mSettings.getPackageLPr("android");
}
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
+ if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails())) {
throw PackageManagerException.ofInternalError("Apps that share a user with a "
+ "privileged app must themselves be marked as privileged. "
+ pkg.getPackageName() + " shares privileged user "
@@ -4839,10 +4835,8 @@
// to allowlist their privileged permissions just like other
// priv-apps.
PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.getSigningDetails().getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
+ if ((compareSignatures(platformPkgSetting.getSigningDetails(),
+ pkg.getSigningDetails()) != PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiverService.java b/services/core/java/com/android/server/pm/PackageArchiver.java
similarity index 97%
rename from services/core/java/com/android/server/pm/PackageArchiverService.java
rename to services/core/java/com/android/server/pm/PackageArchiver.java
index e052407..73ccf5c 100644
--- a/services/core/java/com/android/server/pm/PackageArchiverService.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -31,10 +31,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.IPackageArchiverService;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageArchiver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
@@ -74,7 +72,7 @@
* while the data directory is kept. Archived apps are included in the list of launcher apps where
* tapping them re-installs the full app.
*/
-public class PackageArchiverService extends IPackageArchiverService.Stub {
+public class PackageArchiver {
private static final String TAG = "PackageArchiverService";
@@ -93,13 +91,12 @@
@Nullable
private LauncherApps mLauncherApps;
- public PackageArchiverService(Context context, PackageManagerService mPm) {
+ PackageArchiver(Context context, PackageManagerService mPm) {
this.mContext = context;
this.mPm = mPm;
}
- @Override
- public void requestArchive(
+ void requestArchive(
@NonNull String packageName,
@NonNull String callerPackageName,
@NonNull IntentSender intentSender,
@@ -233,8 +230,7 @@
return true;
}
- @Override
- public void requestUnarchive(
+ void requestUnarchive(
@NonNull String packageName,
@NonNull String callerPackageName,
@NonNull UserHandle userHandle) {
@@ -290,8 +286,8 @@
int userId = userHandle.getIdentifier();
Intent unarchiveIntent = new Intent(Intent.ACTION_UNARCHIVE_PACKAGE);
unarchiveIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- unarchiveIntent.putExtra(PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME, packageName);
- unarchiveIntent.putExtra(PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS,
+ unarchiveIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_PACKAGE_NAME, packageName);
+ unarchiveIntent.putExtra(PackageInstaller.EXTRA_UNARCHIVE_ALL_USERS,
userId == UserHandle.USER_ALL);
unarchiveIntent.setPackage(installerPackage);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fabef76..95b565d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -18,9 +18,7 @@
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
import static android.os.Process.INVALID_UID;
-
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
-
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -182,6 +180,8 @@
Manifest.permission.USE_FULL_SCREEN_INTENT
);
+ final PackageArchiver mPackageArchiver;
+
private final Context mContext;
private final PackageManagerService mPm;
private final ApexManager mApexManager;
@@ -301,6 +301,7 @@
apexParserSupplier, mInstallThread.getLooper());
mGentleUpdateHelper = new GentleUpdateHelper(
context, mInstallThread.getLooper(), new AppStateHelper(context));
+ mPackageArchiver = new PackageArchiver(mContext, mPm);
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
@@ -1504,6 +1505,24 @@
mSilentUpdatePolicy.setSilentUpdatesThrottleTime(throttleTimeInSeconds);
}
+ @Override
+ public void requestArchive(
+ @NonNull String packageName,
+ @NonNull String callerPackageName,
+ @NonNull IntentSender intentSender,
+ @NonNull UserHandle userHandle) {
+ mPackageArchiver.requestArchive(packageName, callerPackageName, intentSender,
+ userHandle);
+ }
+
+ @Override
+ public void requestUnarchive(
+ @NonNull String packageName,
+ @NonNull String callerPackageName,
+ @NonNull UserHandle userHandle) {
+ mPackageArchiver.requestUnarchive(packageName, callerPackageName, userHandle);
+ }
+
private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
int installerUid) {
int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0dd4111ad..9e0a83c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3396,7 +3396,7 @@
}
if (!isInstalledByAdb(getInstallSource().mInitiatingPackageName)
- && !mPm.mArchiverService.verifySupportsUnarchival(
+ && !mPm.mInstallerService.mPackageArchiver.verifySupportsUnarchival(
getInstallSource().mInstallerPackageName)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_SESSION_INVALID,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e3ff6f6b..d23dcbc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -84,6 +84,7 @@
import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
+import android.content.pm.Flags;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
@@ -799,9 +800,6 @@
final SparseArray<VerifyingSession> mPendingEnableRollback = new SparseArray<>();
final PackageInstallerService mInstallerService;
-
- final PackageArchiverService mArchiverService;
-
final ArtManagerService mArtManagerService;
// TODO(b/260124949): Remove these.
@@ -1630,8 +1628,7 @@
(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
context),
- (i, pm) -> new UpdateOwnershipHelper(),
- (i, pm) -> new PackageArchiverService(i.getContext(), pm));
+ (i, pm) -> new UpdateOwnershipHelper());
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1776,7 +1773,6 @@
mFactoryTest = testParams.factoryTest;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
- mArchiverService = testParams.archiverService;
mInstantAppRegistry = testParams.instantAppRegistry;
mChangedPackagesTracker = testParams.changedPackagesTracker;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
@@ -2356,7 +2352,6 @@
});
mInstallerService = mInjector.getPackageInstallerService();
- mArchiverService = mInjector.getPackageArchiverService();
final ComponentName instantAppResolverComponent = getInstantAppResolver(computer);
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
@@ -4621,7 +4616,7 @@
mDomainVerificationConnection, mInstallerService, mPackageProperty,
mResolveComponentName, mInstantAppResolverSettingsComponent,
mRequiredSdkSandboxPackage, mServicesExtensionPackageName,
- mSharedSystemSharedLibraryPackageName, mArchiverService);
+ mSharedSystemSharedLibraryPackageName);
}
@Override
@@ -5926,15 +5921,15 @@
}
}
- Signature[] callerSignature;
+ SigningDetails callerSigningDetails;
final int appId = UserHandle.getAppId(callingUid);
Pair<PackageStateInternal, SharedUserApi> either =
snapshot.getPackageOrSharedUser(appId);
if (either != null) {
if (either.first != null) {
- callerSignature = either.first.getSigningDetails().getSignatures();
+ callerSigningDetails = either.first.getSigningDetails();
} else {
- callerSignature = either.second.getSigningDetails().getSignatures();
+ callerSigningDetails = either.second.getSigningDetails();
}
} else {
throw new SecurityException("Unknown calling UID: " + callingUid);
@@ -5943,8 +5938,8 @@
// Verify: can't set installerPackageName to a package that is
// not signed with the same cert as the caller.
if (installerPackageState != null) {
- if (compareSignatures(callerSignature,
- installerPackageState.getSigningDetails().getSignatures())
+ if (compareSignatures(callerSigningDetails,
+ installerPackageState.getSigningDetails())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -5960,8 +5955,8 @@
? null : snapshot.getPackageStateInternal(targetInstallerPackageName);
if (targetInstallerPkgSetting != null) {
- if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.getSigningDetails().getSignatures())
+ if (compareSignatures(callerSigningDetails,
+ targetInstallerPkgSetting.getSigningDetails())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 9495279..0c2e082 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -127,8 +127,7 @@
mPreparingPackageParserProducer;
private final Singleton<PackageInstallerService>
mPackageInstallerServiceProducer;
- private final Singleton<PackageArchiverService>
- mPackageArchiverServiceProducer;
+
private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
mInstantAppResolverConnectionProducer;
private final Singleton<LegacyPermissionManagerInternal>
@@ -187,8 +186,7 @@
Producer<IBackupManager> iBackupManager,
Producer<SharedLibrariesImpl> sharedLibrariesProducer,
Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
- Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
- Producer<PackageArchiverService> packageArchiverServiceProducer) {
+ Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -244,7 +242,6 @@
mCrossProfileIntentFilterHelperProducer = new Singleton<>(
crossProfileIntentFilterHelperProducer);
mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
- mPackageArchiverServiceProducer = new Singleton<>(packageArchiverServiceProducer);
}
/**
@@ -391,10 +388,6 @@
return mPackageInstallerServiceProducer.get(this, mPackageManager);
}
- public PackageArchiverService getPackageArchiverService() {
- return mPackageArchiverServiceProducer.get(this, mPackageManager);
- }
-
public InstantAppResolverConnection getInstantAppResolverConnection(
ComponentName instantAppResolverComponent) {
return mInstantAppResolverConnectionProducer.produce(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index b91ce4b..ca57209 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -60,7 +60,6 @@
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
- public PackageArchiverService archiverService;
public InstantAppRegistry instantAppRegistry;
public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 2028231..1679987 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -403,7 +403,11 @@
* <br />
* {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
*/
- public static int compareSignatures(Signature[] s1, Signature[] s2) {
+ public static int compareSignatures(SigningDetails sd1, SigningDetails sd2) {
+ return compareSignatureArrays(sd1.getSignatures(), sd2.getSignatures());
+ }
+
+ static int compareSignatureArrays(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -445,10 +449,10 @@
* set or if the signing details of the package are unknown.
*/
public static boolean comparePackageSignatures(PackageSetting pkgSetting,
- Signature[] signatures) {
+ SigningDetails otherSigningDetails) {
final SigningDetails signingDetails = pkgSetting.getSigningDetails();
return signingDetails == SigningDetails.UNKNOWN
- || compareSignatures(signingDetails.getSignatures(), signatures)
+ || compareSignatures(signingDetails, otherSigningDetails)
== PackageManager.SIGNATURE_MATCH;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a0c9cd1..60bd6fe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -334,6 +334,8 @@
return runRenameUser();
case "set-user-restriction":
return runSetUserRestriction();
+ case "get-user-restriction":
+ return runGetUserRestriction();
case "supports-multiple-users":
return runSupportsMultipleUsers();
case "get-max-users":
@@ -3405,6 +3407,51 @@
return 0;
}
+ private int runGetUserRestriction() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_SYSTEM;
+ boolean getAllRestrictions = false;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case "--all":
+ getAllRestrictions = true;
+ if (getNextArg() != null) {
+ throw new IllegalArgumentException("Argument unexpected after \"--all\"");
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_NULL, "runGetUserRestriction");
+ final IUserManager um = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
+
+ if (getAllRestrictions) {
+ final Bundle restrictions = um.getUserRestrictions(translatedUserId);
+ pw.println("All restrictions:");
+ pw.println(restrictions.toString());
+ } else {
+ String restriction = getNextArg();
+ if (restriction == null) {
+ throw new IllegalArgumentException("No restriction key specified");
+ }
+ String unexpectedArgument = getNextArg();
+ if (unexpectedArgument != null) {
+ throw new IllegalArgumentException("Argument unexpected after restriction key");
+ }
+ pw.println(um.hasUserRestriction(restriction, translatedUserId));
+ }
+ return 0;
+ }
+
public int runSupportsMultipleUsers() {
getOutPrintWriter().println("Is multiuser supported: "
+ UserManager.supportsMultipleUsers());
@@ -4788,6 +4835,12 @@
pw.println("");
pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE");
pw.println("");
+ pw.println(" get-user-restriction [--user USER_ID] [--all] RESTRICTION_KEY");
+ pw.println(" Display the value of restriction for the given restriction key if the");
+ pw.println(" given user is valid.");
+ pw.println(" --all: display all restrictions for the given user");
+ pw.println(" This option is used without restriction key");
+ pw.println("");
pw.println(" get-max-users");
pw.println("");
pw.println(" get-max-running-users");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index a8cdef4..cf5aa7b 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -605,7 +605,7 @@
// Check for exact signature matches across all certs.
Signature[] certs = mCerts.toArray(new Signature[0]);
if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
- && !Signature.areExactMatch(certs, pkg.getSigningDetails().getSignatures())) {
+ && !Signature.areExactMatch(pkg.getSigningDetails(), certs)) {
// certs aren't exact match, but the package may have rotated from the known system cert
if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index f4dca3f..0cac790 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -911,8 +911,8 @@
parsedPackage.setSignedWithPlatformKey(
(PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
|| (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
+ platformPkg.getSigningDetails(),
+ parsedPackage.getSigningDetails()
) == PackageManager.SIGNATURE_MATCH))
);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 803b94b..e365e83 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2750,7 +2750,8 @@
}
}
- private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
+ @VisibleForTesting
+ void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
if (!UserRestrictionsUtils.isValidRestriction(key)) {
Slog.e(LOG_TAG, "Setting invalid restriction " + key);
return;
@@ -4360,11 +4361,11 @@
UserRestrictionsUtils.writeRestrictions(serializer,
mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
- TAG_DEVICE_POLICY_RESTRICTIONS);
+ TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
UserRestrictionsUtils.writeRestrictions(serializer,
mDevicePolicyUserRestrictions.getRestrictions(userInfo.id),
- TAG_DEVICE_POLICY_RESTRICTIONS);
+ TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
}
if (userData.account != null) {
diff --git a/services/core/java/com/android/server/pm/flags.aconfig b/services/core/java/com/android/server/pm/flags.aconfig
deleted file mode 100644
index e584801..0000000
--- a/services/core/java/com/android/server/pm/flags.aconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-package: "com.android.server.pm"
-
-flag {
- name: "quarantined_enabled"
- namespace: "package_manager_service"
- description: "Feature flag for Quarantined state"
- bug: "269127435"
-}
-
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index 4e08106..86391c9 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -17,6 +17,7 @@
package com.android.server.pm.pkg;
import android.annotation.Nullable;
+import android.content.pm.Flags;
import android.content.pm.SuspendDialogInfo;
import android.os.BaseBundle;
import android.os.PersistableBundle;
@@ -24,7 +25,6 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.pm.Flags;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 81c2f07..812e228 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -3267,8 +3267,7 @@
if (existingSigningDetails == SigningDetails.UNKNOWN) {
return verified;
} else {
- if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
- verified.getResult().getSignatures())) {
+ if (!Signature.areExactMatch(existingSigningDetails, verified.getResult())) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 03f025b..d0d7f49 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -75,6 +75,18 @@
/** Used to show system bars permanently. This will affect the layout. */
private final InsetsControlTarget mPermanentControlTarget;
+ /**
+ * Used to override the visibility of {@link Type#statusBars()} when dispatching insets to
+ * clients.
+ */
+ private InsetsControlTarget mFakeStatusControlTarget;
+
+ /**
+ * Used to override the visibility of {@link Type#navigationBars()} when dispatching insets to
+ * clients.
+ */
+ private InsetsControlTarget mFakeNavControlTarget;
+
private WindowState mFocusedWin;
private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
@@ -101,25 +113,25 @@
abortTransient();
}
mFocusedWin = focusedWin;
- final InsetsControlTarget statusControlTarget =
- getStatusControlTarget(focusedWin, false /* fake */);
- final InsetsControlTarget navControlTarget =
- getNavControlTarget(focusedWin, false /* fake */);
final WindowState notificationShade = mPolicy.getNotificationShade();
final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
+ final InsetsControlTarget statusControlTarget =
+ getStatusControlTarget(focusedWin, false /* fake */);
+ mFakeStatusControlTarget = statusControlTarget == mTransientControlTarget
+ ? getStatusControlTarget(focusedWin, true /* fake */)
+ : statusControlTarget == notificationShade
+ ? getStatusControlTarget(topApp, true /* fake */)
+ : null;
+ final InsetsControlTarget navControlTarget =
+ getNavControlTarget(focusedWin, false /* fake */);
+ mFakeNavControlTarget = navControlTarget == mTransientControlTarget
+ ? getNavControlTarget(focusedWin, true /* fake */)
+ : navControlTarget == notificationShade
+ ? getNavControlTarget(topApp, true /* fake */)
+ : null;
mStateController.onBarControlTargetChanged(
- statusControlTarget,
- statusControlTarget == mTransientControlTarget
- ? getStatusControlTarget(focusedWin, true /* fake */)
- : statusControlTarget == notificationShade
- ? getStatusControlTarget(topApp, true /* fake */)
- : null,
- navControlTarget,
- navControlTarget == mTransientControlTarget
- ? getNavControlTarget(focusedWin, true /* fake */)
- : navControlTarget == notificationShade
- ? getNavControlTarget(topApp, true /* fake */)
- : null);
+ statusControlTarget, mFakeStatusControlTarget,
+ navControlTarget, mFakeNavControlTarget);
mStatusBar.updateVisibility(statusControlTarget, Type.statusBars());
mNavBar.updateVisibility(navControlTarget, Type.navigationBars());
}
@@ -204,7 +216,7 @@
boolean includesTransient) {
InsetsState state;
if (!includesTransient) {
- state = adjustVisibilityForTransientTypes(originalState);
+ state = adjustVisibilityForFakeControllingSources(originalState);
} else {
state = originalState;
}
@@ -319,24 +331,40 @@
return state;
}
- private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
+ private InsetsState adjustVisibilityForFakeControllingSources(InsetsState originalState) {
+ if (mFakeStatusControlTarget == null && mFakeNavControlTarget == null) {
+ return originalState;
+ }
InsetsState state = originalState;
for (int i = state.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = state.sourceAt(i);
- if (isTransient(source.getType()) && source.isVisible()) {
- if (state == originalState) {
- // The source will be modified, create a non-deep copy to store the new one.
- state = new InsetsState(originalState);
- }
- // Replace the source with a copy in invisible state.
- final InsetsSource outSource = new InsetsSource(source);
- outSource.setVisible(false);
- state.addSource(outSource);
- }
+ state = adjustVisibilityForFakeControllingSource(state, Type.statusBars(), source,
+ mFakeStatusControlTarget);
+ state = adjustVisibilityForFakeControllingSource(state, Type.navigationBars(), source,
+ mFakeNavControlTarget);
}
return state;
}
+ private static InsetsState adjustVisibilityForFakeControllingSource(InsetsState originalState,
+ @InsetsType int type, InsetsSource source, InsetsControlTarget target) {
+ if (source.getType() != type || target == null) {
+ return originalState;
+ }
+ final boolean isRequestedVisible = target.isRequestedVisible(type);
+ if (source.isVisible() == isRequestedVisible) {
+ return originalState;
+ }
+ // The source will be modified, create a non-deep copy to store the new one.
+ final InsetsState state = new InsetsState(originalState);
+
+ // Replace the source with a copy with the overridden visibility.
+ final InsetsSource outSource = new InsetsSource(source);
+ outSource.setVisible(isRequestedVisible);
+ state.addSource(outSource);
+ return state;
+ }
+
private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
boolean copyState) {
if (w.mIsImWindow) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f33ecaa..184de58 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -301,9 +301,12 @@
}
// Always update the reordering time when this is called to ensure that the timeout
- // is reset
+ // is reset. Extend this duration when running in tests.
+ final long timeout = ActivityManager.isRunningInUserTestHarness()
+ ? mFreezeTaskListTimeoutMs * 10
+ : mFreezeTaskListTimeoutMs;
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
- mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
+ mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
}
/**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0c45eea..0674ec1 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -109,7 +109,7 @@
private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
- final boolean mCanForceShowingInsets;
+ boolean mCanForceShowingInsets;
private final boolean mCanStartTasksFromRecents;
final boolean mCanCreateSystemApplicationOverlay;
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index d4d17ec..41706f0 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -24,4 +24,7 @@
"app-compat-annotations",
"service-permission.stubs.system_server",
],
+ static_libs: [
+ "device_policy_aconfig_flags_lib",
+ ],
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 438a9d6..ce133df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -491,6 +491,7 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.DefaultCrossProfileIntentFilter;
@@ -3432,6 +3433,9 @@
}
revertTransferOwnershipIfNecessaryLocked();
+ if (!FlagUtils.isPolicyEngineMigrationV2Enabled()) {
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
}
// In case flag value has changed, we apply it during boot to avoid doing it concurrently
@@ -21575,17 +21579,35 @@
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- synchronized (getLockObject()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- caller.getPackageName(),
- caller.getUserId());
+ if (!FlagUtils.isPolicyEngineMigrationV2Enabled()) {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "USB data signaling can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
"USB data signaling cannot be disabled.");
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- enforcingAdmin,
- new BooleanPolicyValue(enabled));
+ }
+
+ synchronized (getLockObject()) {
+ if (FlagUtils.isPolicyEngineMigrationV2Enabled()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+ caller.getPackageName(),
+ caller.getUserId());
+ Preconditions.checkState(canUsbDataSignalingBeDisabled(),
+ "USB data signaling cannot be disabled.");
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ enforcingAdmin,
+ new BooleanPolicyValue(enabled));
+ } else {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ if (admin.mUsbDataSignalingEnabled != enabled) {
+ admin.mUsbDataSignalingEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
+ }
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -21607,10 +21629,24 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- caller.getUserId());
- return enabled == null || enabled;
+ if (FlagUtils.isPolicyEngineMigrationV2Enabled()) {
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
+ } else {
+ synchronized (getLockObject()) {
+ // If the caller is an admin, return the policy set by itself. Otherwise
+ // return the device-wide policy.
+ if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
+ caller)) {
+ return getProfileOwnerOrDeviceOwnerLocked(
+ caller.getUserId()).mUsbDataSignalingEnabled;
+ } else {
+ return isUsbDataSignalingEnabledInternalLocked();
+ }
+ }
+ }
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index 599c4a7..22464d5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -260,11 +260,14 @@
break;
}
}
- if (admin != null) {
+ if (admin != null && value != null) {
policiesSetByAdmins.put(admin, value);
} else {
- Slogf.wtf(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY, EnforcingAdmin "
- + "is null");
+ Slogf.wtf(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY for "
+ + (policyDefinition == null ? "unknown policy" : "policy with "
+ + "definition " + policyDefinition) + ", EnforcingAdmin is: "
+ + (admin == null ? "null" : admin) + ", value is : "
+ + (value == null ? "null" : value));
}
break;
case TAG_POLICY_DEFINITION_ENTRY:
@@ -283,7 +286,9 @@
}
currentResolvedPolicy = policyDefinition.readPolicyValueFromXml(parser);
if (currentResolvedPolicy == null) {
- Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY, "
+ Slogf.wtf(TAG, "Error Parsing TAG_RESOLVED_VALUE_ENTRY for "
+ + (policyDefinition == null ? "unknown policy" : "policy with "
+ + "definition " + policyDefinition) + ", "
+ "currentResolvedPolicy is null");
}
break;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp b/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp
new file mode 100644
index 0000000..1a45782
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp
@@ -0,0 +1,16 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+ name: "device_policy_aconfig_flags",
+ package: "com.android.server.devicepolicy.flags",
+ srcs: [
+ "flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "device_policy_aconfig_flags_lib",
+ aconfig_declarations: "device_policy_aconfig_flags",
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
new file mode 100644
index 0000000..9fe3749
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy.flags;
+
+import static com.android.server.devicepolicy.flags.Flags.policyEngineMigrationV2Enabled;
+
+import android.os.Binder;
+
+public final class FlagUtils {
+ private FlagUtils(){}
+
+ public static boolean isPolicyEngineMigrationV2Enabled() {
+ return Binder.withCleanCallingIdentity(() -> {
+ return policyEngineMigrationV2Enabled();
+ });
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
new file mode 100644
index 0000000..aff1876
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.devicepolicy.flags"
+
+flag {
+ name: "policy_engine_migration_v2_enabled"
+ namespace: "device_policy"
+ description: "V2 of the policy engine migrations for Android V"
+ bug: "289520697"
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
similarity index 92%
rename from services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
rename to services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 60b28d3..d988063 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -38,7 +38,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageArchiver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
@@ -75,7 +74,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class PackageArchiverServiceTest {
+public class PackageArchiverTest {
private static final String PACKAGE = "com.example";
private static final String CALLER_PACKAGE = "com.caller";
@@ -119,7 +118,7 @@
private PackageSetting mPackageSetting;
- private PackageArchiverService mArchiveService;
+ private PackageArchiver mArchiveManager;
@Before
public void setUp() throws Exception {
@@ -160,8 +159,8 @@
mock(Resources.class));
when(mIcon.compress(eq(Bitmap.CompressFormat.PNG), eq(100), any())).thenReturn(true);
- mArchiveService = spy(new PackageArchiverService(mContext, pm));
- doReturn(ICON_PATH).when(mArchiveService).storeIcon(eq(PACKAGE),
+ mArchiveManager = spy(new PackageArchiver(mContext, pm));
+ doReturn(ICON_PATH).when(mArchiveManager).storeIcon(eq(PACKAGE),
any(LauncherActivityInfo.class), eq(mUserId));
}
@@ -169,7 +168,7 @@
public void archiveApp_callerPackageNameIncorrect() {
Exception e = assertThrows(
SecurityException.class,
- () -> mArchiveService.requestArchive(PACKAGE, "different", mIntentSender,
+ () -> mArchiveManager.requestArchive(PACKAGE, "different", mIntentSender,
UserHandle.CURRENT));
assertThat(e).hasMessageThat().isEqualTo(
String.format(
@@ -186,7 +185,7 @@
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ () -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
@@ -197,7 +196,7 @@
public void archiveApp_packageNotInstalledForUser() throws IntentSender.SendIntentException {
mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false);
- mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -226,7 +225,7 @@
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ () -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo("No installer found");
@@ -239,7 +238,7 @@
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ () -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
@@ -249,10 +248,10 @@
@Test
public void archiveApp_storeIconFails() throws IntentSender.SendIntentException, IOException {
IOException e = new IOException("IO");
- doThrow(e).when(mArchiveService).storeIcon(eq(PACKAGE),
+ doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
any(LauncherActivityInfo.class), eq(mUserId));
- mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -268,7 +267,7 @@
@Test
public void archiveApp_success() {
- mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
verify(mInstallerService).uninstall(
@@ -286,7 +285,7 @@
Exception e = assertThrows(
SecurityException.class,
- () -> mArchiveService.requestUnarchive(PACKAGE, "different",
+ () -> mArchiveManager.requestUnarchive(PACKAGE, "different",
UserHandle.CURRENT));
assertThat(e).hasMessageThat().isEqualTo(
String.format(
@@ -304,7 +303,7 @@
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
@@ -315,7 +314,7 @@
public void unarchiveApp_notArchived() {
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
@@ -338,7 +337,7 @@
Exception e = assertThrows(
ParcelableException.class,
- () -> mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE,
+ () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE,
UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
@@ -349,7 +348,7 @@
public void unarchiveApp_success() {
mUserState.setArchiveState(createArchiveState()).setInstalled(false);
- mArchiveService.requestUnarchive(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT);
+ mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -366,10 +365,10 @@
/* initialExtras= */ isNull());
Intent intent = intentCaptor.getValue();
assertThat(intent.getFlags() & FLAG_RECEIVER_FOREGROUND).isNotEqualTo(0);
- assertThat(intent.getStringExtra(PackageArchiver.EXTRA_UNARCHIVE_PACKAGE_NAME)).isEqualTo(
+ assertThat(intent.getStringExtra(PackageInstaller.EXTRA_UNARCHIVE_PACKAGE_NAME)).isEqualTo(
PACKAGE);
assertThat(
- intent.getBooleanExtra(PackageArchiver.EXTRA_UNARCHIVE_ALL_USERS, true)).isFalse();
+ intent.getBooleanExtra(PackageInstaller.EXTRA_UNARCHIVE_ALL_USERS, true)).isFalse();
assertThat(intent.getPackage()).isEqualTo(INSTALLER_PACKAGE);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index 035bef6..3808f30 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -47,7 +47,9 @@
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -90,8 +92,7 @@
private static final int STREAMED_CALLING_UID = 9876;
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock private Context mMockContext;
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@@ -116,8 +117,6 @@
MockitoAnnotations.initMocks(this);
final Resources resources = InstrumentationRegistry.getContext().getResources();
- mSetFlagsRule.enableFlags(Flags.FLAG_PROXY_USE_APPS_ON_VIRTUAL_DEVICE_LISTENER);
-
mFocusStrokeWidthDefaultValue =
resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color);
@@ -230,6 +229,7 @@
* app changes to the proxy device.
*/
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_PROXY_USE_APPS_ON_VIRTUAL_DEVICE_LISTENER)
public void testUpdateProxyOfRunningAppsChange_changedUidIsStreamedApp_propagatesChange() {
final VirtualDeviceManagerInternal localVdm =
Mockito.mock(VirtualDeviceManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 2273fcd..9f75cf8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -113,6 +113,42 @@
assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false);
}
+ /** Tests that device policy restrictions are written/read properly. */
+ @Test
+ public void testWriteReadDevicePolicyUserRestrictions() throws Exception {
+ final String globalRestriction = UserManager.DISALLOW_FACTORY_RESET;
+ final String localRestriction = UserManager.DISALLOW_CONFIG_DATE_TIME;
+
+ UserData data = new UserData();
+ data.info = createUser(100, FLAG_FULL, "A type");
+
+ mUserManagerService.putUserInfo(data.info);
+
+ // Set a global and user restriction so they get written out to the user file.
+ setUserRestrictions(data.info.id, globalRestriction, localRestriction, true);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ mUserManagerService.writeUserLP(data, out);
+ byte[] bytes = baos.toByteArray();
+
+ // Clear the restrictions to see if they are properly read in from the user file.
+ setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);
+
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes));
+ assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
+ assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction));
+ }
+
+ /** Sets a global and local restriction and verifies they were set properly **/
+ private void setUserRestrictions(int id, String global, String local, boolean enabled) {
+ mUserManagerService.setUserRestrictionInner(UserHandle.USER_ALL, global, enabled);
+ assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(global), enabled);
+
+ mUserManagerService.setUserRestrictionInner(id, local, enabled);
+ assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(local), enabled);
+ }
+
@Test
public void testParcelUnparcelUserInfo() throws Exception {
UserInfo info = createUser();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 411712e..ffa1ed9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -327,6 +327,7 @@
addNavigationBar().getControllableInsetProvider().getSource();
statusBarSource.setVisible(false);
navBarSource.setVisible(false);
+ mAppWindow.setRequestedVisibleTypes(0, navigationBars() | statusBars());
mAppWindow.mAboveInsetsState.addSource(navBarSource);
mAppWindow.mAboveInsetsState.addSource(statusBarSource);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
@@ -387,6 +388,50 @@
assertFalse(policy.isTransient(navigationBars()));
}
+ @Test
+ public void testFakeControlTarget_overrideVisibilityReceivedByWindows() {
+ final WindowState statusBar = addStatusBar();
+ final InsetsSourceProvider statusBarProvider = statusBar.getControllableInsetProvider();
+ statusBar.mSession.mCanForceShowingInsets = true;
+ statusBar.setHasSurface(true);
+ statusBarProvider.setServerVisible(true);
+
+ final InsetsSource statusBarSource = statusBarProvider.getSource();
+ final int statusBarId = statusBarSource.getId();
+ assertTrue(statusBarSource.isVisible());
+
+ final WindowState app1 = addWindow(TYPE_APPLICATION, "app1");
+ app1.mAboveInsetsState.addSource(statusBarSource);
+ assertTrue(app1.getInsetsState().peekSource(statusBarId).isVisible());
+
+ final WindowState app2 = addWindow(TYPE_APPLICATION, "app2");
+ app2.mAboveInsetsState.addSource(statusBarSource);
+ assertTrue(app2.getInsetsState().peekSource(statusBarId).isVisible());
+
+ app2.setRequestedVisibleTypes(0, navigationBars() | statusBars());
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(app2);
+ waitUntilWindowAnimatorIdle();
+
+ // app2 is the real control target now. It can override the visibility of all sources that
+ // it controls.
+ assertFalse(statusBarSource.isVisible());
+ assertFalse(app1.getInsetsState().peekSource(statusBarId).isVisible());
+ assertFalse(app2.getInsetsState().peekSource(statusBarId).isVisible());
+
+ statusBar.mAttrs.forciblyShownTypes = statusBars();
+ mDisplayContent.getDisplayPolicy().applyPostLayoutPolicyLw(
+ statusBar, statusBar.mAttrs, null, null);
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(app2);
+ waitUntilWindowAnimatorIdle();
+
+ // app2 is the fake control target now. It can only override the visibility of sources
+ // received by windows, but not the raw source.
+ assertTrue(statusBarSource.isVisible());
+ assertFalse(app1.getInsetsState().peekSource(statusBarId).isVisible());
+ assertFalse(app2.getInsetsState().peekSource(statusBarId).isVisible());
+
+ }
+
private WindowState addNavigationBar() {
final Binder owner = new Binder();
final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 114796d..2085d61 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -367,9 +367,11 @@
doReturn(rotatedState).when(app.mToken).getFixedRotationTransformInsetsState();
assertTrue(rotatedState.isSourceOrDefaultVisible(ID_STATUS_BAR, statusBars()));
- provider.getSource().setVisible(false);
+ app.setRequestedVisibleTypes(0, statusBars());
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(app);
mDisplayContent.getInsetsPolicy().showTransient(statusBars(),
true /* isGestureOnSystemBar */);
+ waitUntilWindowAnimatorIdle();
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(statusBars()));
assertFalse(app.getInsetsState().isSourceOrDefaultVisible(ID_STATUS_BAR, statusBars()));
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
index 3870dfd..4f99f14 100644
--- a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
@@ -95,21 +95,29 @@
String newTaskId = cinematicEffectRequest.getTaskId();
// Previous request is still being processed.
if (mCinematicEffectListenerWrapper != null) {
+ CinematicEffectResponse cinematicEffectResponse;
if (mCinematicEffectListenerWrapper.mTaskId.equals(newTaskId)) {
- invokeCinematicListenerAndCleanup(
- new CinematicEffectResponse.Builder(
- CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId)
- .build()
- );
+ cinematicEffectResponse = new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId)
+ .build();
} else {
- invokeCinematicListenerAndCleanup(
- new CinematicEffectResponse.Builder(
- CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS,
- newTaskId).build()
- );
+ cinematicEffectResponse = new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS,
+ newTaskId)
+ .build();
}
- return;
+ try {
+ cinematicEffectListener.onCinematicEffectGenerated(cinematicEffectResponse);
+ return;
+ } catch (RemoteException e) {
+ if (isDebug()) {
+ Slog.w(TAG, "RemoteException invoking cinematic effect listener for task["
+ + mCinematicEffectListenerWrapper.mTaskId + "]");
+ }
+ return;
+ }
}
+
RemoteWallpaperEffectsGenerationService remoteService = ensureRemoteServiceLocked();
if (remoteService != null) {
remoteService.executeOnResolvedService(