Merge "Fix testRespectTopFullscreenOrientation failed on tablet" into main
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
index bc8fc53..2de6f36 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -147,28 +147,4 @@
out.clear();
}
}
-
- @ManualBenchmarkState.ManualBenchmarkTest(
- warmupDurationNs = WARMUP_DURATION_NS,
- targetTestDurationNs = TARGET_TEST_DURATION_NS)
- @Test
- public void testSetSystemFontMap() throws Exception {
- SharedMemory memory = null;
- ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();
-
- long elapsedTime = 0;
- while (state.keepRunning(elapsedTime)) {
- // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
- // (max_map_count).
- Typeface.destroySystemFontMap();
- Typeface.loadPreinstalledSystemFontMap();
- if (memory != null) {
- memory.close();
- }
- memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
- long startTime = System.nanoTime();
- Typeface.setSystemFontMap(memory);
- elapsedTime = System.nanoTime() - startTime;
- }
- }
}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 9a0053f..22cc059 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -483,6 +483,22 @@
}
java_library {
+ name: "android_test_frameworks_core_stubs_current.from-source",
+ static_libs: [
+ "all-updatable-modules-system-stubs",
+ "android-non-updatable.stubs.test",
+ "private-stub-annotations-jar",
+ ],
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test-core",
+ },
+}
+
+java_library {
name: "android_module_lib_stubs_current.from-source",
defaults: [
"android.jar_defaults",
diff --git a/api/api.go b/api/api.go
index c568a45..d5c6145 100644
--- a/api/api.go
+++ b/api/api.go
@@ -110,6 +110,7 @@
Api_surface *string
Api_contributions []string
Defaults_visibility []string
+ Previous_api *string
}
type Bazel_module struct {
@@ -145,7 +146,7 @@
metalavaCmd := "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
- metalavaCmd += " --quiet --no-banner --format=v2 "
+ metalavaCmd += " --quiet merge-signatures --format=v2 "
filename := txt.TxtFilename
if txt.Scope != "public" {
@@ -155,7 +156,7 @@
props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
props.Tools = []string{"metalava"}
props.Out = []string{filename}
- props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+ props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
props.Dists = []android.Dist{
{
@@ -359,6 +360,7 @@
props.Api_contributions = transformArray(
modules, "", fmt.Sprintf(".stubs.source%s.api.contribution", apiSuffix))
props.Defaults_visibility = []string{"//visibility:public"}
+ props.Previous_api = proptools.StringPtr(":android.api.public.latest")
ctx.CreateModule(java.DefaultsFactory, &props)
}
}
@@ -368,6 +370,7 @@
"android_stubs_current",
"android_system_stubs_current",
"android_test_stubs_current",
+ "android_test_frameworks_core_stubs_current",
"android_module_lib_stubs_current",
"android_system_server_stubs_current",
}
diff --git a/core/api/current.txt b/core/api/current.txt
index d1b5154..9ba84c9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33668,6 +33668,7 @@
public static class PerformanceHintManager.Session implements java.io.Closeable {
method public void close();
method public void reportActualWorkDuration(long);
+ method public void setPreferPowerEfficiency(boolean);
method public void setThreads(@NonNull int[]);
method public void updateTargetWorkDuration(long);
}
@@ -47270,9 +47271,9 @@
}
public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
- 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);
+ 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);
method public void ellipsized(int, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
@@ -47285,12 +47286,12 @@
method public int getLineTop(int);
method public int getParagraphDirection(int);
method public int getTopPadding();
- 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 @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 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);
@@ -47315,7 +47316,6 @@
method public int getBottomPadding();
method public int getEllipsisCount(int);
method public int getEllipsisStart(int);
- method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
method public boolean getLineContainsTab(int);
method public int getLineCount();
method public int getLineDescent(int);
@@ -47488,20 +47488,26 @@
method public void drawBackground(@NonNull android.graphics.Canvas);
method public void drawText(@NonNull android.graphics.Canvas);
method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
- method public final android.text.Layout.Alignment getAlignment();
+ method @NonNull public final android.text.Layout.Alignment getAlignment();
method public abstract int getBottomPadding();
+ method 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 public int getEllipsizedWidth();
+ method @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 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 public abstract boolean getLineContainsTab(int);
method public abstract int getLineCount();
method public abstract int getLineDescent(int);
@@ -47512,29 +47518,35 @@
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 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 public int getOffsetForHorizontal(int, float);
method public int getOffsetToLeftOf(int);
method public int getOffsetToRightOf(int);
- method public final android.text.TextPaint getPaint();
+ method @NonNull public final android.text.TextPaint getPaint();
method public final android.text.Layout.Alignment getParagraphAlignment(int);
method public abstract int getParagraphDirection(int);
method public final int getParagraphLeft(int);
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 public float getSecondaryHorizontal(int);
method public void getSelectionPath(int, int, android.graphics.Path);
method public final float getSpacingAdd();
method public final float getSpacingMultiplier();
- method public final CharSequence getText();
+ method @NonNull public final CharSequence getText();
+ method @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
method public abstract int getTopPadding();
- method public final int getWidth();
+ method @IntRange(from=0) public final int getWidth();
method public final void increaseWidthTo(int);
method public boolean isFallbackLineSpacingEnabled();
+ method public final boolean isIncludeFontPadding();
method public boolean isRtlCharAt(int);
method protected final boolean isSpanned();
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -47562,6 +47574,26 @@
enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE;
}
+ 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);
+ method @NonNull public android.text.Layout.Builder setBreakStrategy(int);
+ method @NonNull public android.text.Layout.Builder setEllipsize(@Nullable android.text.TextUtils.TruncateAt);
+ method @NonNull public android.text.Layout.Builder setEllipsizedWidth(@IntRange(from=0) int);
+ method @NonNull public android.text.Layout.Builder setFallbackLineSpacingEnabled(boolean);
+ method @NonNull public android.text.Layout.Builder setHyphenationFrequency(int);
+ method @NonNull public android.text.Layout.Builder setIncludeFontPadding(boolean);
+ method @NonNull public android.text.Layout.Builder setJustificationMode(int);
+ method @NonNull public android.text.Layout.Builder setLeftIndents(@Nullable int[]);
+ method @NonNull public android.text.Layout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
+ method @NonNull public android.text.Layout.Builder setLineSpacingAmount(float);
+ method @NonNull public android.text.Layout.Builder setLineSpacingMultiplier(float);
+ 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);
+ }
+
public static class Layout.Directions {
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d480315..6141583 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10389,6 +10389,7 @@
field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
+ field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3429c7c..e61c39f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2358,6 +2358,7 @@
public final class PowerManager {
method public boolean areAutoPowerSaveModesEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
+ method public boolean isBatterySaverSupported();
field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9121cf0..7be4b15 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -179,6 +179,10 @@
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
+ @NonNull
+ @GuardedBy("mPackageMonitorCallbacks")
+ private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
+
UserManager getUserManager() {
if (mUserManager == null) {
mUserManager = UserManager.get(mContext);
@@ -3926,6 +3930,14 @@
Objects.requireNonNull(callback);
try {
mPM.registerPackageMonitorCallback(callback, userId);
+ synchronized (mPackageMonitorCallbacks) {
+ if (mPackageMonitorCallbacks.contains(callback)) {
+ throw new IllegalStateException(
+ "registerPackageMonitorCallback: callback already registered: "
+ + callback);
+ }
+ mPackageMonitorCallbacks.add(callback);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3936,6 +3948,9 @@
Objects.requireNonNull(callback);
try {
mPM.unregisterPackageMonitorCallback(callback);
+ synchronized (mPackageMonitorCallbacks) {
+ mPackageMonitorCallbacks.remove(callback);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index 763246e..dc66542 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.AuthenticateOptions;
+import android.hardware.biometrics.common.AuthenticateReason;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
@@ -85,7 +86,16 @@
return null;
}
-
+ /**
+ * The Vendor extension, if any.
+ *
+ * This option may be present when a vendor would like to send additional information for each
+ * auth attempt.
+ */
+ @Nullable private AuthenticateReason.Vendor mVendorReason;
+ private static AuthenticateReason.Vendor defaultVendorReason() {
+ return null;
+ }
// Code below generated by codegen v1.0.23.
//
@@ -107,7 +117,8 @@
boolean ignoreEnrollmentState,
@AuthenticateOptions.DisplayState int displayState,
@NonNull String opPackageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag,
+ @Nullable AuthenticateReason.Vendor vendorReason) {
this.mUserId = userId;
this.mSensorId = sensorId;
this.mIgnoreEnrollmentState = ignoreEnrollmentState;
@@ -118,6 +129,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
+ this.mVendorReason = vendorReason;
// onConstructed(); // You can define this method to get a callback
}
@@ -176,6 +188,17 @@
}
/**
+ * The Vendor extension, if any.
+ *
+ * This option may be present when a vendor would like to send additional information for each
+ * auth attempt.
+ */
+ @DataClass.Generated.Member
+ public @Nullable AuthenticateReason.Vendor getVendorReason() {
+ return mVendorReason;
+ }
+
+ /**
* The sensor id for this operation.
*/
@DataClass.Generated.Member
@@ -209,6 +232,18 @@
return this;
}
+ /**
+ * The Vendor extension, if any.
+ *
+ * This option may be present when a vendor would like to send additional information for each
+ * auth attempt.
+ */
+ @DataClass.Generated.Member
+ public @NonNull FingerprintAuthenticateOptions setVendorReason(@NonNull AuthenticateReason.Vendor value) {
+ mVendorReason = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -227,7 +262,8 @@
&& mIgnoreEnrollmentState == that.mIgnoreEnrollmentState
&& mDisplayState == that.mDisplayState
&& java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
- && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+ && java.util.Objects.equals(mVendorReason, that.mVendorReason);
}
@Override
@@ -243,6 +279,7 @@
_hash = 31 * _hash + mDisplayState;
_hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason);
return _hash;
}
@@ -255,12 +292,14 @@
byte flg = 0;
if (mIgnoreEnrollmentState) flg |= 0x4;
if (mAttributionTag != null) flg |= 0x20;
+ if (mVendorReason != null) flg |= 0x40;
dest.writeByte(flg);
dest.writeInt(mUserId);
dest.writeInt(mSensorId);
dest.writeInt(mDisplayState);
dest.writeString(mOpPackageName);
if (mAttributionTag != null) dest.writeString(mAttributionTag);
+ if (mVendorReason != null) dest.writeTypedObject(mVendorReason, flags);
}
@Override
@@ -281,6 +320,7 @@
int displayState = in.readInt();
String opPackageName = in.readString();
String attributionTag = (flg & 0x20) == 0 ? null : in.readString();
+ AuthenticateReason.Vendor vendorReason = (flg & 0x40) == 0 ? null : (AuthenticateReason.Vendor) in.readTypedObject(AuthenticateReason.Vendor.CREATOR);
this.mUserId = userId;
this.mSensorId = sensorId;
@@ -292,6 +332,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOpPackageName);
this.mAttributionTag = attributionTag;
+ this.mVendorReason = vendorReason;
// onConstructed(); // You can define this method to get a callback
}
@@ -323,6 +364,7 @@
private @AuthenticateOptions.DisplayState int mDisplayState;
private @NonNull String mOpPackageName;
private @Nullable String mAttributionTag;
+ private @Nullable AuthenticateReason.Vendor mVendorReason;
private long mBuilderFieldsSet = 0L;
@@ -400,10 +442,24 @@
return this;
}
+ /**
+ * The Vendor extension, if any.
+ *
+ * This option may be present when a vendor would like to send additional information for each
+ * auth attempt.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setVendorReason(@NonNull AuthenticateReason.Vendor value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mVendorReason = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FingerprintAuthenticateOptions build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x40; // Mark builder used
+ mBuilderFieldsSet |= 0x80; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mUserId = defaultUserId();
@@ -423,18 +479,22 @@
if ((mBuilderFieldsSet & 0x20) == 0) {
mAttributionTag = defaultAttributionTag();
}
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mVendorReason = defaultVendorReason();
+ }
FingerprintAuthenticateOptions o = new FingerprintAuthenticateOptions(
mUserId,
mSensorId,
mIgnoreEnrollmentState,
mDisplayState,
mOpPackageName,
- mAttributionTag);
+ mAttributionTag,
+ mVendorReason);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x40) != 0) {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -442,10 +502,10 @@
}
@DataClass.Generated(
- time = 1677119626721L,
+ time = 1689703591032L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
- inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 43219bc..42c5626 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1656,6 +1656,8 @@
public abstract CpuScalingPolicies getCpuScalingPolicies();
public final static class HistoryTag {
+ public static final int HISTORY_TAG_POOL_OVERFLOW = -1;
+
public String string;
public int uid;
@@ -6826,10 +6828,11 @@
if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) {
didWake = true;
sb.append("=");
- if (longNames) {
+ if (longNames
+ || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
UserHandle.formatUid(sb, wakelockTag.uid);
sb.append(":\"");
- sb.append(wakelockTag.string);
+ sb.append(wakelockTag.string.replace("\"", "\"\""));
sb.append("\"");
} else {
sb.append(wakelockTag.poolIdx);
@@ -6849,7 +6852,7 @@
}
if (!didWake && wakelockTag != null) {
sb.append(longNames ? " wake_lock=" : ",w=");
- if (longNames) {
+ if (longNames || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
UserHandle.formatUid(sb, wakelockTag.uid);
sb.append(":\"");
sb.append(wakelockTag.string);
@@ -7110,7 +7113,14 @@
if (rec.wakeReasonTag != null) {
if (checkin) {
item.append(",wr=");
- item.append(rec.wakeReasonTag.poolIdx);
+ if (rec.wakeReasonTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+ item.append(sUidToString.applyAsString(rec.wakeReasonTag.uid));
+ item.append(":\"");
+ item.append(rec.wakeReasonTag.string.replace("\"", "\"\""));
+ item.append("\"");
+ } else {
+ item.append(rec.wakeReasonTag.poolIdx);
+ }
} else {
item.append(" wake_reason=");
item.append(rec.wakeReasonTag.uid);
@@ -7138,7 +7148,15 @@
}
item.append("=");
if (checkin) {
- item.append(rec.eventTag.poolIdx);
+ if (rec.eventTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+ item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
+ .applyAsString(rec.eventTag.uid));
+ item.append(":\"");
+ item.append(rec.eventTag.string.replace("\"", "\"\""));
+ item.append("\"");
+ } else {
+ item.append(rec.eventTag.poolIdx);
+ }
} else {
item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
.applyAsString(rec.eventTag.uid));
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index d9d14b0..0456a33 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -81,7 +81,8 @@
BUGREPORT_MODE_REMOTE,
BUGREPORT_MODE_WEAR,
BUGREPORT_MODE_TELEPHONY,
- BUGREPORT_MODE_WIFI
+ BUGREPORT_MODE_WIFI,
+ BUGREPORT_MODE_ONBOARDING
})
public @interface BugreportMode {}
@@ -121,6 +122,11 @@
public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
/**
+ * Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+ */
+ public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
+
+ /**
* Defines acceptable flags for customizing bugreport requests.
* @hide
*/
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 0d1dde1..6b43e73 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -23,4 +23,5 @@
void reportActualWorkDuration(in long[] actualDurationNanos, in long[] timeStampNanos);
void close();
void sendHint(int hint);
+ void setMode(int mode, boolean enabled);
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index be7c1e5..dbb6f92 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -60,6 +60,7 @@
boolean isPowerSaveMode();
PowerSaveState getPowerSaveState(int serviceType);
boolean setPowerSaveModeEnabled(boolean mode);
+ boolean isBatterySaverSupported();
BatterySaverPolicyConfig getFullPowerSavePolicy();
boolean setFullPowerSavePolicy(in BatterySaverPolicyConfig config);
boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold);
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index bcea797..cbc9213 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -51,6 +51,15 @@
}
/**
+ * Get preferred update rate information for this device.
+ *
+ * @return the preferred update rate supported by device software
+ */
+ public long getPreferredUpdateRateNanos() {
+ return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
+ }
+
+ /**
* Creates a {@link Session} for the given set of threads and sets their initial target work
* duration.
*
@@ -78,35 +87,22 @@
}
/**
- * Get preferred update rate information for this device.
- *
- * @return the preferred update rate supported by device software
- */
- public long getPreferredUpdateRateNanos() {
- return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
- }
-
- /**
* A Session represents a group of threads with an inter-related workload such that hints for
* their performance should be considered as a unit. The threads in a given session should be
- * long-life and not created or destroyed dynamically.
+ * long-lived and not created or destroyed dynamically.
*
- * <p>Each session is expected to have a periodic workload with a target duration for each
- * cycle. The cycle duration is likely greater than the target work duration to allow other
- * parts of the pipeline to run within the available budget. For example, a renderer thread may
- * work at 60hz in order to produce frames at the display's frame but have a target work
- * duration of only 6ms.</p>
+ * The work duration API can be used with periodic workloads to dynamically adjust thread
+ * performance and keep the work on schedule while optimizing the available power budget.
+ * When using the work duration API, the starting target duration should be specified
+ * while creating the session, but can later be adjusted with
+ * {@link #updateTargetWorkDuration(long)}. While using the work duration API, the client is be
+ * expected to call {@link #reportActualWorkDuration(long)} each cycle to report the actual
+ * time taken to complete to the system.
*
- * <p>Any call in this class will change its internal data, so you must do your own thread
- * safety to protect from racing.</p>
+ * Any call in this class will change its internal data, so you must do your own thread
+ * safety to protect from racing.
*
- * <p>Note that the target work duration can be {@link #updateTargetWorkDuration(long) updated}
- * if workloads change.</p>
- *
- * <p>After each cycle of work, the client is expected to
- * {@link #reportActualWorkDuration(long) report} the actual time taken to complete.</p>
- *
- * <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
+ * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.
*/
public static class Session implements Closeable {
private long mNativeSessionPtr;
@@ -186,9 +182,9 @@
/**
* Reports the actual duration for the last cycle of work.
*
- * <p>The system will attempt to adjust the core placement of the threads within the thread
+ * The system will attempt to adjust the core placement of the threads within the thread
* group and/or the frequency of the core on which they are run to bring the actual duration
- * close to the target duration.</p>
+ * close to the target duration.
*
* @param actualDurationNanos how long the thread group took to complete its last task in
* nanoseconds
@@ -202,7 +198,7 @@
/**
* Ends the current hint session.
*
- * <p>Once called, you should not call anything else on this object.</p>
+ * Once called, you should not call anything else on this object.
*/
public void close() {
if (mNativeSessionPtr != 0) {
@@ -230,6 +226,16 @@
}
/**
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param enabled The flag that sets whether this session uses power-efficient scheduling.
+ */
+ public void setPreferPowerEfficiency(boolean enabled) {
+ nativeSetPreferPowerEfficiency(mNativeSessionPtr, enabled);
+ }
+
+ /**
* Set a list of threads to the performance hint session. This operation will replace
* the current list of threads with the given list of threads.
* Note that this is not an oneway method.
@@ -275,4 +281,6 @@
private static native void nativeCloseSession(long nativeSessionPtr);
private static native void nativeSendHint(long nativeSessionPtr, int hint);
private static native void nativeSetThreads(long nativeSessionPtr, int[] tids);
+ private static native void nativeSetPreferPowerEfficiency(long nativeSessionPtr,
+ boolean enabled);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d676509..4174c1c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1936,6 +1936,20 @@
}
/**
+ * Returns true if Battery Saver is supported on this device.
+ *
+ * @hide
+ */
+ @TestApi
+ public boolean isBatterySaverSupported() {
+ try {
+ return mService.isBatterySaverSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the current policy for full power save mode.
*
* @return The {@link BatterySaverPolicyConfig} which is currently set for the full power save
diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java
index ebdd463..5fb0df7 100644
--- a/core/java/android/os/PowerMonitor.java
+++ b/core/java/android/os/PowerMonitor.java
@@ -23,6 +23,10 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an
+ * EnergyConsumer, as defined in
+ * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a>
+ *
* @hide
*/
public final class PowerMonitor implements Parcelable {
@@ -92,6 +96,7 @@
return 0;
}
+ @NonNull
public static final Creator<PowerMonitor> CREATOR = new Creator<>() {
@Override
public PowerMonitor createFromParcel(@NonNull Parcel in) {
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index 3d7f859..e767059 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import java.util.Arrays;
@@ -43,8 +44,8 @@
* @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index
* @hide
*/
- public PowerMonitorReadings(PowerMonitor[] powerMonitors,
- long[] energyUws, long[] timestampsMs) {
+ public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
+ @NonNull long[] energyUws, @NonNull long[] timestampsMs) {
mPowerMonitors = powerMonitors;
mEnergyUws = energyUws;
mTimestampsMs = timestampsMs;
@@ -55,7 +56,7 @@
* Does not persist across reboots.
* Represents total energy: both on-battery and plugged-in.
*/
- public long getConsumedEnergyUws(PowerMonitor powerMonitor) {
+ public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) {
int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
if (offset >= 0) {
return mEnergyUws[offset];
@@ -64,9 +65,10 @@
}
/**
- * Elapsed realtime when the snapshot was taken.
+ * Elapsed realtime, in milliseconds, when the snapshot was taken.
*/
- public long getTimestampMs(PowerMonitor powerMonitor) {
+ @ElapsedRealtimeLong
+ public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
if (offset >= 0) {
return mTimestampsMs[offset];
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 180735b..81d4e3a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1462,7 +1462,7 @@
if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) {
// Detect nothing extra
- } else if (Build.IS_USERDEBUG) {
+ } else if (Build.IS_USERDEBUG || Build.IS_ENG) {
// Detect everything in bundled apps
if (isBundledSystemApp(ai)) {
builder.detectAll();
@@ -1470,14 +1470,9 @@
if (SystemProperties.getBoolean(VISUAL_PROPERTY, false)) {
builder.penaltyFlashScreen();
}
- }
- } else if (Build.IS_ENG) {
- // Detect everything in bundled apps
- if (isBundledSystemApp(ai)) {
- builder.detectAll();
- builder.penaltyDropBox();
- builder.penaltyLog();
- builder.penaltyFlashScreen();
+ if (Build.IS_ENG) {
+ builder.penaltyLog();
+ }
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ba1f979..d8cf4de 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4618,6 +4618,26 @@
}
/**
+ * Returns number of full users on the device.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ public int getFullUserCount() {
+ List<UserInfo> users = getUsers(/* excludePartial= */ true, /* excludeDying= */ true,
+ /* excludePreCreated= */ true);
+ int count = 0;
+ for (UserInfo user : users) {
+ if (user.isFull()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
* @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or
* {@link #getUsers()} for @code getUsers(false)}.
*
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index ab30a8b..dfc43f4 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -24,6 +24,8 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
import android.os.IPowerStatsService;
import android.os.PowerMonitor;
import android.os.PowerMonitorReadings;
@@ -36,8 +38,8 @@
import java.util.Arrays;
import java.util.Comparator;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
+import java.util.List;
+import java.util.function.Consumer;
/**
* Provides access to data about how various system resources are used by applications.
@@ -62,7 +64,8 @@
private final IBatteryStats mBatteryStats;
@Nullable
private final IPowerStatsService mPowerStats;
- private PowerMonitor[] mPowerMonitorsInfo;
+ private List<PowerMonitor> mPowerMonitorsInfo;
+ private final Object mPowerMonitorsLock = new Object();
/**
* Construct a new SystemHealthManager object.
@@ -161,53 +164,68 @@
* @hide
*/
@NonNull
- public PowerMonitor[] getSupportedPowerMonitors() {
- synchronized (this) {
+ public List<PowerMonitor> getSupportedPowerMonitors() {
+ synchronized (mPowerMonitorsLock) {
if (mPowerMonitorsInfo != null) {
return mPowerMonitorsInfo;
}
+ }
+ ConditionVariable lock = new ConditionVariable();
+ // Populate mPowerMonitorsInfo by side-effect
+ getSupportedPowerMonitors(null, unused -> lock.open());
+ lock.block();
- CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>();
- getSupportedPowerMonitors(future);
- try {
- return future.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
+ synchronized (mPowerMonitorsLock) {
+ return mPowerMonitorsInfo;
}
}
/**
- * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()}
+ * Asynchronously retrieves a list of supported power monitors, see
+ * {@link #getSupportedPowerMonitors()}
+ *
+ * @param handler optional Handler to deliver the callback. If not supplied, the callback
+ * may be invoked on an arbitrary thread.
+ * @param onResult callback for the result
*
* @hide
*/
- public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) {
- synchronized (this) {
+ public void getSupportedPowerMonitors(@Nullable Handler handler,
+ @NonNull Consumer<List<PowerMonitor>> onResult) {
+ final List<PowerMonitor> result;
+ synchronized (mPowerMonitorsLock) {
if (mPowerMonitorsInfo != null) {
- future.complete(mPowerMonitorsInfo);
- return;
+ result = mPowerMonitorsInfo;
+ } else if (mPowerStats == null) {
+ mPowerMonitorsInfo = List.of();
+ result = mPowerMonitorsInfo;
+ } else {
+ result = null;
}
- try {
- if (mPowerStats == null) {
- mPowerMonitorsInfo = new PowerMonitor[0];
- future.complete(mPowerMonitorsInfo);
- return;
- }
-
- mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- synchronized (this) {
- mPowerMonitorsInfo = resultData.getParcelableArray(
- IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
- }
- future.complete(mPowerMonitorsInfo);
+ }
+ if (result != null) {
+ if (handler != null) {
+ handler.post(() -> onResult.accept(result));
+ } else {
+ onResult.accept(result);
+ }
+ return;
+ }
+ try {
+ mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ PowerMonitor[] array = resultData.getParcelableArray(
+ IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
+ List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of();
+ synchronized (mPowerMonitorsLock) {
+ mPowerMonitorsInfo = result;
}
- });
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ onResult.accept(result);
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -215,54 +233,74 @@
* Retrieves the accumulated power consumption reported by the specified power monitors.
*
* @param powerMonitors power monitors to be returned.
+ *
* @hide
*/
@NonNull
- public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) {
- CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>();
- getPowerMonitorReadings(powerMonitors, future);
- try {
- return future.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
+ public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) {
+ PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1];
+ RuntimeException[] outException = new RuntimeException[1];
+ ConditionVariable lock = new ConditionVariable();
+ getPowerMonitorReadings(powerMonitors, null,
+ pms -> {
+ outReadings[0] = pms;
+ lock.open();
+ },
+ error -> {
+ outException[0] = error;
+ lock.open();
+ }
+ );
+ lock.block();
+ if (outException[0] != null) {
+ throw outException[0];
}
+ return outReadings[0];
}
private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
Comparator.comparingInt(pm -> pm.index);
/**
+ * Asynchronously retrieves the accumulated power consumption reported by the specified power
+ * monitors.
+ *
* @param powerMonitors power monitors to be retrieved.
+ * @param handler optional Handler to deliver the callbacks. If not supplied, the callback
+ * may be invoked on an arbitrary thread.
+ * @param onSuccess callback for the result
+ * @param onError callback invoked in case of an error
+ *
* @hide
*/
- public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
- @NonNull CompletableFuture<PowerMonitorReadings> future) {
+ public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
+ @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
+ @NonNull Consumer<RuntimeException> onError) {
if (mPowerStats == null) {
- future.completeExceptionally(
- new IllegalArgumentException("Unsupported power monitor"));
+ onError.accept(new IllegalArgumentException("Unsupported power monitor"));
return;
}
- Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR);
- int[] indices = new int[powerMonitors.length];
- for (int i = 0; i < powerMonitors.length; i++) {
- indices[i] = powerMonitors[i].index;
+ PowerMonitor[] powerMonitorsArray =
+ powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]);
+ Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR);
+ int[] indices = new int[powerMonitors.size()];
+ for (int i = 0; i < powerMonitors.size(); i++) {
+ indices[i] = powerMonitorsArray[i].index;
}
try {
- mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) {
+ mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
- future.complete(new PowerMonitorReadings(powerMonitors,
+ onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray,
resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)));
} else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
- future.completeExceptionally(
- new IllegalArgumentException("Unsupported power monitor"));
+ onError.accept(new IllegalArgumentException("Unsupported power monitor"));
} else {
- future.completeExceptionally(
- new IllegalStateException(
- "Unrecognized result code " + resultCode));
+ onError.accept(new IllegalStateException(
+ "Unrecognized result code " + resultCode));
}
}
});
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index abf50a2..3f2be51 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11265,6 +11265,19 @@
"navigation_mode";
/**
+ * The value is from another(source) device's {@link #NAVIGATION_MODE} during restore.
+ * It's supposed to be written only by
+ * {@link com.android.providers.settings.SettingsHelper}.
+ * This setting should not be added into backup array.
+ * <p>Value: -1 = Can't get value from restore(default),
+ * 0 = 3 button,
+ * 1 = 2 button,
+ * 2 = fully gestural.
+ * @hide
+ */
+ public static final String NAVIGATION_MODE_RESTORE = "navigation_mode_restore";
+
+ /**
* Scale factor for the back gesture inset size on the left side of the screen.
* @hide
*/
@@ -18616,6 +18629,7 @@
* What OS does paired device has.
* @hide
*/
+ @Readable
public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type";
// Possible values of PAIRED_DEVICE_OS_TYPE
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index b85cf6d..fce87db 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -285,6 +286,7 @@
private IControlsSubscriber mCs;
private boolean mEnforceStateless;
private Context mContext;
+ private SubscriptionAdapter mSubscription;
SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) {
mEnforceStateless = enforceStateless;
@@ -300,11 +302,14 @@
public void onSubscribe(Subscription subscription) {
try {
- mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
+ SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(subscription);
+ mCs.onSubscribe(mToken, subscriptionAdapter);
+ mSubscription = subscriptionAdapter;
} catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
+ handleRemoteException(ex);
}
}
+
public void onNext(@NonNull Control control) {
Preconditions.checkNotNull(control);
try {
@@ -318,20 +323,36 @@
}
mCs.onNext(mToken, control);
} catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
+ handleRemoteException(ex);
}
}
+
public void onError(Throwable t) {
try {
mCs.onError(mToken, t.toString());
+ mSubscription = null;
} catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
+ handleRemoteException(ex);
}
}
+
public void onComplete() {
try {
mCs.onComplete(mToken);
+ mSubscription = null;
} catch (RemoteException ex) {
+ handleRemoteException(ex);
+ }
+ }
+
+ private void handleRemoteException(RemoteException ex) {
+ if (ex instanceof DeadObjectException) {
+ // System UI crashed or is restarting. There is no need to rethrow this
+ SubscriptionAdapter subscriptionAdapter = mSubscription;
+ if (subscriptionAdapter != null) {
+ subscriptionAdapter.cancel();
+ }
+ } else {
ex.rethrowAsRuntimeException();
}
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 9ed57c3..7748d5b 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -23,6 +23,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.text.LineBreakConfig;
import android.text.style.ParagraphStyle;
/**
@@ -53,7 +54,9 @@
* 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) {
@@ -79,7 +82,9 @@
* @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) {
@@ -111,7 +116,9 @@
* 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,
@@ -247,10 +254,17 @@
* 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, spacingMult, spacingAdd);
+ super(source, paint, outerwidth, align, TextDirectionHeuristics.LTR, spacingMult,
+ spacingAdd, includePad, false /* fallbackLineSpacing */,
+ outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */,
+ BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
+ null /* rightIndents */, JUSTIFICATION_MODE_NONE,
+ LineBreakConfig.NONE);
mEllipsizedWidth = outerwidth;
mEllipsizedStart = 0;
@@ -277,7 +291,9 @@
* @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) {
@@ -306,7 +322,9 @@
* 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,
@@ -318,25 +336,58 @@
* but we can't use "this" for the callback until the call to
* super() finishes.
*/
- super(source, paint, outerWidth, align, spacingMult, spacingAdd);
+ this(source, paint, outerWidth, align, TextDirectionHeuristics.LTR, spacingMult,
+ spacingAdd, includePad, useFallbackLineSpacing,
+ ellipsizedWidth, ellipsize, 1 /* maxLines */,
+ BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
+ null /* rightIndents */, JUSTIFICATION_MODE_NONE,
+ LineBreakConfig.NONE, metrics);
+ }
+
+ /* package */ BoringLayout(
+ CharSequence text,
+ TextPaint paint,
+ int width,
+ Alignment align,
+ TextDirectionHeuristic textDir,
+ float spacingMult,
+ float spacingAdd,
+ boolean includePad,
+ boolean fallbackLineSpacing,
+ int ellipsizedWidth,
+ TextUtils.TruncateAt ellipsize,
+ int maxLines,
+ int breakStrategy,
+ int hyphenationFrequency,
+ int[] leftIndents,
+ int[] rightIndents,
+ int justificationMode,
+ LineBreakConfig lineBreakConfig,
+ Metrics metrics) {
+
+ super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad,
+ fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy,
+ hyphenationFrequency, leftIndents, rightIndents, justificationMode,
+ lineBreakConfig);
+
boolean trust;
if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
- mEllipsizedWidth = outerWidth;
+ mEllipsizedWidth = width;
mEllipsizedStart = 0;
mEllipsizedCount = 0;
trust = true;
} else {
- replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
- paint, outerWidth, align, spacingMult, spacingAdd);
+ replaceWith(TextUtils.ellipsize(text, paint, ellipsizedWidth, ellipsize, true, this),
+ paint, width, align, spacingMult, spacingAdd);
mEllipsizedWidth = ellipsizedWidth;
trust = false;
}
- mUseFallbackLineSpacing = useFallbackLineSpacing;
- init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
+ mUseFallbackLineSpacing = fallbackLineSpacing;
+ init(getText(), paint, align, metrics, includePad, trust, fallbackLineSpacing);
}
/* package */ void init(CharSequence source, TextPaint paint, Alignment align,
@@ -391,7 +442,9 @@
* @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);
}
@@ -406,7 +459,9 @@
* @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);
}
@@ -466,7 +521,9 @@
* 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) {
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index ba08f25..3bdaca9 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -388,7 +388,11 @@
@Nullable TextUtils.TruncateAt ellipsize,
@IntRange(from = 0) int ellipsizedWidth) {
super(createEllipsizer(ellipsize, display),
- paint, width, align, textDir, spacingmult, spacingadd);
+ paint, width, align, textDir, spacingmult, spacingadd, includepad,
+ false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize,
+ Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency,
+ null /* leftIndents */, null /* rightIndents */, justificationMode,
+ lineBreakConfig);
final Builder b = Builder.obtain(base, paint, width)
.setAlignment(align)
@@ -410,7 +414,11 @@
private DynamicLayout(@NonNull Builder b) {
super(createEllipsizer(b.mEllipsize, b.mDisplay),
- b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
+ b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
+ b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
+ Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency,
+ null /* leftIndents */, null /* rightIndents */, b.mJustificationMode,
+ b.mLineBreakConfig);
mDisplay = b.mDisplay;
mIncludePad = b.mIncludePad;
@@ -615,7 +623,6 @@
}
if (reflowed == null) {
- reflowed = new StaticLayout(null);
b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
}
@@ -631,9 +638,10 @@
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
.setLineBreakConfig(mLineBreakConfig)
- .setAddLastLineLineSpacing(!islast);
+ .setAddLastLineLineSpacing(!islast)
+ .setIncludePad(false);
- reflowed.generate(b, false /*includepad*/, true /*trackpad*/);
+ reflowed = b.regenerate(true /* trackpadding */, reflowed);
int n = reflowed.getLineCount();
// If the new layout has a blank line at the end, but it is not
// the very end of the buffer, then we already have a line that
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 64dc16d..98b9fee 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -26,6 +26,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.text.LineBreakConfig;
import android.graphics.text.LineBreaker;
import android.os.Build;
import android.text.method.TextKeyListener;
@@ -278,7 +279,9 @@
int width, Alignment align,
float spacingMult, float spacingAdd) {
this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
- spacingMult, spacingAdd);
+ spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE,
+ BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null,
+ JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE);
}
/**
@@ -290,15 +293,44 @@
* @param width the wrapping width for the text.
* @param align whether to left, right, or center the text. Styles can
* override the alignment.
+ * @param textDir a text direction heuristic.
* @param spacingMult factor by which to scale the font size to get the
* default line spacing
* @param spacingAdd amount to add to the default line spacing
+ * @param includePad true for enabling including font padding
+ * @param fallbackLineSpacing true for enabling fallback line spacing
+ * @param ellipsizedWidth width as used for ellipsizing purpose
+ * @param ellipsize an ellipsize option
+ * @param maxLines a maximum number of lines.
+ * @param breakStrategy a break strategy.
+ * @param hyphenationFrequency a hyphenation frequency
+ * @param leftIndents a visually left margins
+ * @param rightIndents a visually right margins
+ * @param justificationMode a justification mode
+ * @param lineBreakConfig a line break config
*
* @hide
*/
- protected Layout(CharSequence text, TextPaint paint,
- int width, Alignment align, TextDirectionHeuristic textDir,
- float spacingMult, float spacingAdd) {
+ protected Layout(
+ CharSequence text,
+ TextPaint paint,
+ int width,
+ Alignment align,
+ TextDirectionHeuristic textDir,
+ float spacingMult,
+ float spacingAdd,
+ boolean includePad,
+ boolean fallbackLineSpacing,
+ int ellipsizedWidth,
+ TextUtils.TruncateAt ellipsize,
+ int maxLines,
+ int breakStrategy,
+ int hyphenationFrequency,
+ int[] leftIndents,
+ int[] rightIndents,
+ int justificationMode,
+ LineBreakConfig lineBreakConfig
+ ) {
if (width < 0)
throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -320,11 +352,17 @@
mSpacingAdd = spacingAdd;
mSpannedText = text instanceof Spanned;
mTextDir = textDir;
- }
-
- /** @hide */
- protected void setJustificationMode(@JustificationMode int justificationMode) {
+ mIncludePad = includePad;
+ mFallbackLineSpacing = fallbackLineSpacing;
+ mEllipsizedWidth = ellipsize == null ? width : ellipsizedWidth;
+ mEllipsize = ellipsize;
+ mMaxLines = maxLines;
+ mBreakStrategy = breakStrategy;
+ mHyphenationFrequency = hyphenationFrequency;
+ mLeftIndents = leftIndents;
+ mRightIndents = rightIndents;
mJustificationMode = justificationMode;
+ mLineBreakConfig = lineBreakConfig;
}
/**
@@ -910,37 +948,6 @@
}
/**
- * Return the text that is displayed by this Layout.
- */
- public final CharSequence getText() {
- return mText;
- }
-
- /**
- * Return the base Paint properties for this layout.
- * Do NOT change the paint, which may result in funny
- * drawing for this layout.
- */
- public final TextPaint getPaint() {
- return mPaint;
- }
-
- /**
- * Return the width of this layout.
- */
- public final int getWidth() {
- return mWidth;
- }
-
- /**
- * Return the width to which this Layout is ellipsizing, or
- * {@link #getWidth} if it is not doing anything special.
- */
- public int getEllipsizedWidth() {
- return mWidth;
- }
-
- /**
* Increase the width of this layout to the specified width.
* Be careful to use this only when you know it is appropriate—
* it does not cause the text to reflow to use the full new width.
@@ -972,35 +979,6 @@
}
/**
- * Return the base alignment of this layout.
- */
- public final Alignment getAlignment() {
- return mAlignment;
- }
-
- /**
- * Return what the text height is multiplied by to get the line height.
- */
- public final float getSpacingMultiplier() {
- return mSpacingMult;
- }
-
- /**
- * Return the number of units of leading that are added to each line.
- */
- public final float getSpacingAdd() {
- return mSpacingAdd;
- }
-
- /**
- * Return the heuristic used to determine paragraph text direction.
- * @hide
- */
- public final TextDirectionHeuristic getTextDirectionHeuristic() {
- return mTextDir;
- }
-
- /**
* Return the number of lines of text in this layout.
*/
public abstract int getLineCount();
@@ -1105,15 +1083,6 @@
}
/**
- * Return true if the fallback line space is enabled in this Layout.
- *
- * @return true if the fallback line space is enabled. Otherwise returns false.
- */
- public boolean isFallbackLineSpacingEnabled() {
- return false;
- }
-
- /**
* Returns true if the character at offset and the preceding character
* are at different run levels (and thus there's a split caret).
* @param offset the offset
@@ -3254,7 +3223,17 @@
private boolean mSpannedText;
private TextDirectionHeuristic mTextDir;
private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
+ private boolean mIncludePad;
+ private boolean mFallbackLineSpacing;
+ private int mEllipsizedWidth;
+ private TextUtils.TruncateAt mEllipsize;
+ private int mMaxLines;
+ private int mBreakStrategy;
+ private int mHyphenationFrequency;
+ private int[] mLeftIndents;
+ private int[] mRightIndents;
private int mJustificationMode;
+ private LineBreakConfig mLineBreakConfig;
/** @hide */
@IntDef(prefix = { "DIR_" }, value = {
@@ -3352,4 +3331,683 @@
*/
boolean isSegmentInside(@NonNull RectF segmentBounds, @NonNull RectF area);
}
+
+ /**
+ * A builder class for Layout object.
+ *
+ * Different from {@link StaticLayout.Builder}, this builder generates the optimal layout based
+ * on input. If the given text and parameters can be rendered with {@link BoringLayout}, this
+ * builder generates {@link BoringLayout} instance. Otherwise, {@link StaticLayout} instance is
+ * generated.
+ *
+ * @see StaticLayout.Builder
+ */
+ public static final class Builder {
+ /**
+ * Construct a builder class.
+ *
+ * @param text a text to be displayed.
+ * @param start an inclusive start index of the text to be displayed.
+ * @param end an exclusive end index of the text to be displayed.
+ * @param paint a paint object to be used for drawing text.
+ * @param width a width constraint in pixels.
+ */
+ public Builder(
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextPaint paint,
+ @IntRange(from = 0) int width) {
+ mText = text;
+ mStart = start;
+ mEnd = end;
+ mPaint = paint;
+ mWidth = width;
+ mEllipsizedWidth = width;
+ }
+
+ /**
+ * Set the text alignment.
+ *
+ * The default value is {@link Layout.Alignment#ALIGN_NORMAL}.
+ *
+ * @param alignment an alignment.
+ * @return this builder instance.
+ * @see Layout.Alignment
+ * @see Layout#getAlignment()
+ * @see StaticLayout.Builder#setAlignment(Alignment)
+ */
+ @NonNull
+ public Builder setAlignment(@NonNull Alignment alignment) {
+ mAlignment = alignment;
+ return this;
+ }
+
+ /**
+ * Set the text direction heuristics.
+ *
+ * The text direction heuristics is used to resolve text direction on the text.
+ *
+ * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}
+ *
+ * @param textDirection a text direction heuristic.
+ * @return this builder instance.
+ * @see TextDirectionHeuristics
+ * @see Layout#getTextDirectionHeuristic()
+ * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic)
+ */
+ @NonNull
+ public Builder setTextDirectionHeuristic(@NonNull TextDirectionHeuristic textDirection) {
+ mTextDir = textDirection;
+ return this;
+ }
+
+ /**
+ * Set the line spacing amount.
+ *
+ * The specified amount of pixels will be added to each line.
+ *
+ * The default value is {@code 0}.
+ *
+ * @param amount an amount of pixels to be added to line height.
+ * @return this builder instance.
+ * @see Layout#getLineSpacingAmount()
+ * @see Layout#getSpacingAdd()
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ */
+ @NonNull
+ public Builder setLineSpacingAmount(float amount) {
+ mSpacingAdd = amount;
+ return this;
+ }
+
+ /**
+ * Set the line spacing multiplier.
+ *
+ * The specified value will be multiplied to each line.
+ *
+ * The default value is {@code 1}.
+ *
+ * @param multiplier a multiplier to be applied to the line height
+ * @return this builder instance.
+ * @see Layout#getLineSpacingMultiplier()
+ * @see Layout#getSpacingMultiplier()
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ */
+ @NonNull
+ public Builder setLineSpacingMultiplier(float multiplier) {
+ mSpacingMult = multiplier;
+ return this;
+ }
+
+ /**
+ * Set whether including extra padding into the first and the last line height.
+ *
+ * By setting true, the first line of the text and the last line of the text will have extra
+ * vertical space for avoiding clipping.
+ *
+ * The default value is {@code true}.
+ *
+ * @param includeFontPadding true for including extra space into first and last line.
+ * @return this builder instance.
+ * @see Layout#isIncludeFontPadding()
+ * @see StaticLayout.Builder#setIncludePad(boolean)
+ */
+ @NonNull
+ public Builder setIncludeFontPadding(boolean includeFontPadding) {
+ mIncludePad = includeFontPadding;
+ return this;
+ }
+
+ /**
+ * Set whether to respect the ascent and descent of the fallback fonts.
+ *
+ * Set whether to respect the ascent and descent of the fallback fonts that are used in
+ * displaying the text (which is needed to avoid text from consecutive lines running into
+ * each other). If set, fallback fonts that end up getting used can increase the ascent
+ * and descent of the lines that they are used on.
+ *
+ * The default value is {@code false}
+ *
+ * @param fallbackLineSpacing whether to expand line height based on fallback fonts.
+ * @return this builder instance.
+ * @see Layout#isFallbackLineSpacingEnabled()
+ * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
+ */
+ @NonNull
+ public Builder setFallbackLineSpacingEnabled(boolean fallbackLineSpacing) {
+ mFallbackLineSpacing = fallbackLineSpacing;
+ return this;
+ }
+
+ /**
+ * Set the width as used for ellipsizing purpose in pixels.
+ *
+ * The passed value is ignored and forced to set to the value of width constraint passed in
+ * constructor if no ellipsize option is set.
+ *
+ * The default value is the width constraint.
+ *
+ * @param ellipsizeWidth a ellipsizing width in pixels.
+ * @return this builder instance.
+ * @see Layout#getEllipsizedWidth()
+ * @see StaticLayout.Builder#setEllipsizedWidth(int)
+ */
+ @NonNull
+ public Builder setEllipsizedWidth(@IntRange(from = 0) int ellipsizeWidth) {
+ mEllipsizedWidth = ellipsizeWidth;
+ return this;
+ }
+
+ /**
+ * Set the ellipsizing type.
+ *
+ * By setting null, the ellipsize is disabled.
+ *
+ * The default value is {@code null}.
+ *
+ * @param ellipsize type of the ellipsize. null for disabling ellipsize.
+ * @return this builder instance.
+ * @see Layout#getEllipsize()
+ * @see StaticLayout.Builder#getEllipsize()
+ * @see android.text.TextUtils.TruncateAt
+ */
+ @NonNull
+ public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
+ mEllipsize = ellipsize;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of lines.
+ *
+ * The default value is unlimited.
+ *
+ * @param maxLines maximum number of lines in the layout.
+ * @return this builder instance.
+ * @see Layout#getMaxLines()
+ * @see StaticLayout.Builder#setMaxLines(int)
+ */
+ @NonNull
+ public Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+ mMaxLines = maxLines;
+ return this;
+ }
+
+ /**
+ * Set the line break strategy.
+ *
+ * The default value is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+ *
+ * @param breakStrategy a break strategy for line breaking.
+ * @return this builder instance.
+ * @see Layout#getBreakStrategy()
+ * @see StaticLayout.Builder#setBreakStrategy(int)
+ * @see Layout#BREAK_STRATEGY_SIMPLE
+ * @see Layout#BREAK_STRATEGY_HIGH_QUALITY
+ * @see Layout#BREAK_STRATEGY_BALANCED
+ */
+ @NonNull
+ public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
+ mBreakStrategy = breakStrategy;
+ return this;
+ }
+
+ /**
+ * Set the hyphenation frequency.
+ *
+ * The default value is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
+ *
+ * @param hyphenationFrequency a hyphenation frequency.
+ * @return this builder instance.
+ * @see Layout#getHyphenationFrequency()
+ * @see StaticLayout.Builder#setHyphenationFrequency(int)
+ * @see Layout#HYPHENATION_FREQUENCY_NONE
+ * @see Layout#HYPHENATION_FREQUENCY_NORMAL
+ * @see Layout#HYPHENATION_FREQUENCY_FULL
+ * @see Layout#HYPHENATION_FREQUENCY_NORMAL_FAST
+ * @see Layout#HYPHENATION_FREQUENCY_FULL_FAST
+ */
+ @NonNull
+ public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
+ mHyphenationFrequency = hyphenationFrequency;
+ return this;
+ }
+
+ /**
+ * Set visually left indents in pixels per lines.
+ *
+ * For the lines past the last element in the array, the last element repeats. Passing null
+ * for disabling indents.
+ *
+ * Note that even with the RTL layout, this method reserve spacing at the visually left of
+ * the line.
+ *
+ * The default value is {@code null}.
+ *
+ * @param leftIndents array of indents values for the left margins in pixels.
+ * @return this builder instance.
+ * @see Layout#getLeftIndents()
+ * @see Layout#getRightIndents()
+ * @see Layout.Builder#setRightIndents(int[])
+ * @see StaticLayout.Builder#setIndents(int[], int[])
+ */
+ @NonNull
+ public Builder setLeftIndents(@Nullable int[] leftIndents) {
+ mLeftIndents = leftIndents;
+ return this;
+ }
+
+ /**
+ * Set visually right indents in pixels per lines.
+ *
+ * For the lines past the last element in the array, the last element repeats. Passing null
+ * for disabling indents.
+ *
+ * Note that even with the RTL layout, this method reserve spacing at the visually right of
+ * the line.
+ *
+ * The default value is {@code null}.
+ *
+ * @param rightIndents array of indents values for the right margins in pixels.
+ * @return this builder instance.
+ * @see Layout#getLeftIndents()
+ * @see Layout#getRightIndents()
+ * @see Layout.Builder#setLeftIndents(int[])
+ * @see StaticLayout.Builder#setIndents(int[], int[])
+ */
+ @NonNull
+ public Builder setRightIndents(@Nullable int[] rightIndents) {
+ mRightIndents = rightIndents;
+ return this;
+ }
+
+ /**
+ * Set justification mode.
+ *
+ * When justification mode is {@link Layout#JUSTIFICATION_MODE_INTER_WORD}, the word spacing
+ * on the given Paint passed to the constructor will be ignored. This behavior also affects
+ * spans which change the word spacing.
+ *
+ * The default value is {@link Layout#JUSTIFICATION_MODE_NONE}.
+ *
+ * @param justificationMode justification mode.
+ * @return this builder instance.
+ * @see Layout#getJustificationMode()
+ * @see StaticLayout.Builder#setJustificationMode(int)
+ * @see Layout#JUSTIFICATION_MODE_NONE
+ * @see Layout#JUSTIFICATION_MODE_INTER_WORD
+ */
+ @NonNull
+ public Builder setJustificationMode(@JustificationMode int justificationMode) {
+ mJustificationMode = justificationMode;
+ return this;
+ }
+
+ /**
+ * Set the line break configuration.
+ *
+ * The default value is a LinebreakConfig instance that has
+ * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and
+ * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}.
+ *
+ * @param lineBreakConfig the line break configuration
+ * @return this builder instance.
+ * @see Layout#getLineBreakConfig()
+ * @see StaticLayout.Builder#setLineBreakConfig(LineBreakConfig)
+ */
+ @NonNull
+ public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+ mLineBreakConfig = lineBreakConfig;
+ return this;
+ }
+
+ private BoringLayout.Metrics isBoring() {
+ if (mStart != 0 || mEnd != mText.length()) { // BoringLayout only support entire text.
+ return null;
+ }
+ BoringLayout.Metrics metrics = BoringLayout.isBoring(mText, mPaint, mTextDir,
+ mFallbackLineSpacing, null);
+ if (metrics == null) {
+ return null;
+ }
+ if (metrics.width <= mWidth) {
+ return metrics;
+ }
+ if (mEllipsize != null) {
+ return metrics;
+ }
+ return null;
+ }
+
+ /**
+ * Build a Layout object.
+ */
+ @NonNull
+ public Layout build() {
+ BoringLayout.Metrics metrics = isBoring();
+ if (metrics == null) { // we cannot use BoringLayout, create StaticLayout.
+ return StaticLayout.Builder.obtain(mText, mStart, mEnd, mPaint, mWidth)
+ .setAlignment(mAlignment)
+ .setLineSpacing(mSpacingAdd, mSpacingMult)
+ .setTextDirection(mTextDir)
+ .setIncludePad(mIncludePad)
+ .setUseLineSpacingFromFallbacks(mFallbackLineSpacing)
+ .setEllipsizedWidth(mEllipsizedWidth)
+ .setEllipsize(mEllipsize)
+ .setMaxLines(mMaxLines)
+ .setBreakStrategy(mBreakStrategy)
+ .setHyphenationFrequency(mHyphenationFrequency)
+ .setIndents(mLeftIndents, mRightIndents)
+ .setJustificationMode(mJustificationMode)
+ .setLineBreakConfig(mLineBreakConfig)
+ .build();
+ } else {
+ return new BoringLayout(
+ mText, mPaint, mWidth, mAlignment, mTextDir, mSpacingMult, mSpacingAdd,
+ mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines,
+ mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents,
+ mJustificationMode, mLineBreakConfig, metrics);
+ }
+ }
+
+ private final CharSequence mText;
+ private final int mStart;
+ private final int mEnd;
+ private final TextPaint mPaint;
+ private final int mWidth;
+ private Alignment mAlignment = Alignment.ALIGN_NORMAL;
+ private float mSpacingMult = 1.0f;
+ private float mSpacingAdd = 0.0f;
+ private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+ private boolean mIncludePad = true;
+ private boolean mFallbackLineSpacing = false;
+ private int mEllipsizedWidth;
+ private TextUtils.TruncateAt mEllipsize = null;
+ private int mMaxLines = Integer.MAX_VALUE;
+ private int mBreakStrategy = BREAK_STRATEGY_SIMPLE;
+ private int mHyphenationFrequency = HYPHENATION_FREQUENCY_NONE;
+ private int[] mLeftIndents = null;
+ private int[] mRightIndents = null;
+ private int mJustificationMode = JUSTIFICATION_MODE_NONE;
+ private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Getters of parameters that is used for building Layout instance
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Return the text used for creating this layout.
+ *
+ * @return the text used for creating this layout.
+ * @see Layout.Builder
+ */
+ @NonNull
+ public final CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Return the paint used for creating this layout.
+ *
+ * Do not modify the returned paint object. This paint object will still be used for
+ * drawing/measuring text.
+ *
+ * @return the paint used for creating this layout.
+ * @see Layout.Builder
+ */
+ @NonNull
+ public final TextPaint getPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Return the width used for creating this layout in pixels.
+ *
+ * @return the width used for creating this layout in pixels.
+ * @see Layout.Builder
+ */
+ @IntRange(from = 0)
+ public final int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Returns the alignment used for creating this layout in pixels.
+ *
+ * @return the alignment used for creating this layout.
+ * @see Layout.Builder#setAlignment(Alignment)
+ * @see StaticLayout.Builder#setAlignment(Alignment)
+ */
+ @NonNull
+ public final Alignment getAlignment() {
+ return mAlignment;
+ }
+
+ /**
+ * Returns the text direction heuristic used for creating this layout.
+ *
+ * @return the text direction heuristic used for creating this layout
+ * @see Layout.Builder#setTextDirectionHeuristic(TextDirectionHeuristic)
+ * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic)
+ */
+ @NonNull
+ public final TextDirectionHeuristic getTextDirectionHeuristic() {
+ return mTextDir;
+ }
+
+ /**
+ * Returns the multiplier applied to the line height.
+ *
+ * This is an alias of {@link #getLineSpacingMultiplier}.
+ *
+ * @return the line height multiplier.
+ * @see Layout.Builder#setLineSpacingMultiplier(float)
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ * @see Layout#getLineSpacingMultiplier()
+ */
+ public final float getSpacingMultiplier() {
+ return getLineSpacingMultiplier();
+ }
+
+ /**
+ * Returns the multiplier applied to the line height.
+ *
+ * @return the line height multiplier.
+ * @see Layout.Builder#setLineSpacingMultiplier(float)
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ * @see Layout#getSpacingMultiplier()
+ */
+ public final float getLineSpacingMultiplier() {
+ return mSpacingMult;
+ }
+
+ /**
+ * Returns the amount added to the line height.
+ *
+ * This is an alias of {@link #getLineSpacingAmount()}.
+ *
+ * @return the line height additional amount.
+ * @see Layout.Builder#setLineSpacingAmount(float)
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ * @see Layout#getLineSpacingAmount()
+ */
+ public final float getSpacingAdd() {
+ return getLineSpacingAmount();
+ }
+
+ /**
+ * Returns the amount added to the line height.
+ *
+ * @return the line height additional amount.
+ * @see Layout.Builder#setLineSpacingAmount(float)
+ * @see StaticLayout.Builder#setLineSpacing(float, float)
+ * @see Layout#getSpacingAdd()
+ */
+ public final float getLineSpacingAmount() {
+ return mSpacingAdd;
+ }
+
+ /**
+ * Returns true if this layout is created with increased line height.
+ *
+ * @return true if the layout is created with increased line height.
+ * @see Layout.Builder#setIncludeFontPadding(boolean)
+ * @see StaticLayout.Builder#setIncludePad(boolean)
+ */
+ public final boolean isIncludeFontPadding() {
+ return mIncludePad;
+ }
+
+ /**
+ * Return true if the fallback line space is enabled in this Layout.
+ *
+ * @return true if the fallback line space is enabled. Otherwise, returns false.
+ * @see Layout.Builder#setFallbackLineSpacingEnabled(boolean)
+ * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
+ */
+ // not being final because of already published API.
+ public boolean isFallbackLineSpacingEnabled() {
+ return mFallbackLineSpacing;
+ }
+
+ /**
+ * Return the width to which this layout is ellipsized.
+ *
+ * If no ellipsize is applied, the same amount of {@link #getWidth} is returned.
+ *
+ * @return the amount of ellipsized width in pixels.
+ * @see Layout.Builder#setEllipsizedWidth(int)
+ * @see StaticLayout.Builder#setEllipsizedWidth(int)
+ * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt)
+ * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt)
+ * @see Layout#getEllipsize()
+ */
+ @IntRange(from = 0)
+ public int getEllipsizedWidth() { // not being final because of already published API.
+ return mEllipsizedWidth;
+ }
+
+ /**
+ * Return the ellipsize option used for creating this layout.
+ *
+ * May return null if no ellipsize option was selected.
+ *
+ * @return The ellipsize option used for creating this layout, or null if no ellipsize option
+ * was selected.
+ * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt)
+ * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt)
+ * @see Layout.Builder#setEllipsizedWidth(int)
+ * @see StaticLayout.Builder#setEllipsizedWidth(int)
+ * @see Layout#getEllipsizedWidth()
+ */
+ @Nullable
+ public final TextUtils.TruncateAt getEllipsize() {
+ return mEllipsize;
+ }
+
+ /**
+ * Return the maximum lines allowed used for creating this layout.
+ *
+ * Note that this is not an actual line count of this layout. Use {@link #getLineCount()} for
+ * getting the actual line count of this layout.
+ *
+ * @return the maximum lines allowed used for creating this layout.
+ * @see Layout.Builder#setMaxLines(int)
+ * @see StaticLayout.Builder#setMaxLines(int)
+ */
+ @IntRange(from = 1)
+ public final int getMaxLines() {
+ return mMaxLines;
+ }
+
+ /**
+ * Return the break strategy used for creating this layout.
+ *
+ * @return the break strategy used for creating this layout.
+ * @see Layout.Builder#setBreakStrategy(int)
+ * @see StaticLayout.Builder#setBreakStrategy(int)
+ */
+ @BreakStrategy
+ public final int getBreakStrategy() {
+ return mBreakStrategy;
+ }
+
+ /**
+ * Return the hyphenation frequency used for creating this layout.
+ *
+ * @return the hyphenation frequency used for creating this layout.
+ * @see Layout.Builder#setHyphenationFrequency(int)
+ * @see StaticLayout.Builder#setHyphenationFrequency(int)
+ */
+ @HyphenationFrequency
+ public final int getHyphenationFrequency() {
+ return mHyphenationFrequency;
+ }
+
+ /**
+ * Return a copy of the left indents used for this layout.
+ *
+ * May return null if no left indentation is applied.
+ *
+ * @return the array of left indents in pixels.
+ * @see Layout.Builder#setLeftIndents(int[])
+ * @see Layout.Builder#setRightIndents(int[])
+ * @see StaticLayout.Builder#setIndents(int[], int[])
+ */
+ @Nullable
+ public final int[] getLeftIndents() {
+ if (mLeftIndents == null) {
+ return null;
+ }
+ int[] newArray = new int[mLeftIndents.length];
+ System.arraycopy(mLeftIndents, 0, newArray, 0, newArray.length);
+ return newArray;
+ }
+
+ /**
+ * Return a copy of the right indents used for this layout.
+ *
+ * May return null if no right indentation is applied.
+ *
+ * @return the array of right indents in pixels.
+ * @see Layout.Builder#setLeftIndents(int[])
+ * @see Layout.Builder#setRightIndents(int[])
+ * @see StaticLayout.Builder#setIndents(int[], int[])
+ */
+ @Nullable
+ public final int[] getRightIndents() {
+ if (mRightIndents == null) {
+ return mLeftIndents;
+ }
+ int[] newArray = new int[mRightIndents.length];
+ System.arraycopy(mRightIndents, 0, newArray, 0, newArray.length);
+ return newArray;
+ }
+
+ /**
+ * Return the justification mode used for creating this layout.
+ *
+ * @return the justification mode used for creating this layout.
+ * @see Layout.Builder#setJustificationMode(int)
+ * @see StaticLayout.Builder#setJustificationMode(int)
+ */
+ @JustificationMode
+ public final int getJustificationMode() {
+ return mJustificationMode;
+ }
+
+ /**
+ * Gets the {@link LineBreakConfig} used for creating this layout.
+ *
+ * Do not modify the returned object.
+ *
+ * @return The line break config used for creating this layout.
+ */
+ // not being final because of subclass has already published API.
+ @NonNull
+ public LineBreakConfig getLineBreakConfig() {
+ return mLineBreakConfig;
+ }
}
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index a6be687..0935ffd9 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,5 +1,6 @@
set noparent
+grantapher@google.com
halilibo@google.com
haoyuchang@google.com
justinghan@google.com
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ab9cff0..f843900 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -431,11 +431,21 @@
*/
@NonNull
public StaticLayout build() {
- StaticLayout result = new StaticLayout(this);
+ StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null
+ ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
Builder.recycle(this);
return result;
}
+ /* package */ @NonNull StaticLayout regenerate(boolean trackpadding, StaticLayout recycle) {
+ if (recycle == null) {
+ return new StaticLayout(this, trackpadding, COLUMNS_ELLIPSIZE);
+ } else {
+ recycle.generate(this, mIncludePad, trackpadding);
+ return recycle;
+ }
+ }
+
private CharSequence mText;
private int mStart;
private int mEnd;
@@ -515,88 +525,32 @@
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
- super((ellipsize == null)
- ? source
- : (source instanceof Spanned)
- ? new SpannedEllipsizer(source)
- : new Ellipsizer(source),
- paint, outerwidth, align, textDir, spacingmult, spacingadd);
-
- Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
- .setAlignment(align)
- .setTextDirection(textDir)
- .setLineSpacing(spacingadd, spacingmult)
- .setIncludePad(includepad)
- .setEllipsizedWidth(ellipsizedWidth)
- .setEllipsize(ellipsize)
- .setMaxLines(maxLines);
- /*
- * This is annoying, but we can't refer to the layout until superclass construction is
- * finished, and the superclass constructor wants the reference to the display text.
- *
- * In other words, the two Ellipsizer classes in Layout.java need a (Dynamic|Static)Layout
- * as a parameter to do their calculations, but the Ellipsizers also need to be the input
- * to the superclass's constructor (Layout). In order to go around the circular
- * dependency, we construct the Ellipsizer with only one of the parameters, the text. And
- * we fill in the rest of the needed information (layout, width, and method) later, here.
- *
- * This will break if the superclass constructor ever actually cares about the content
- * instead of just holding the reference.
- */
- if (ellipsize != null) {
- Ellipsizer e = (Ellipsizer) getText();
-
- e.mLayout = this;
- e.mWidth = ellipsizedWidth;
- e.mMethod = ellipsize;
- mEllipsizedWidth = ellipsizedWidth;
-
- mColumns = COLUMNS_ELLIPSIZE;
- } else {
- mColumns = COLUMNS_NORMAL;
- mEllipsizedWidth = outerwidth;
- }
-
- mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
- mLines = ArrayUtils.newUnpaddedIntArray(2 * mColumns);
- mMaximumVisibleLineCount = maxLines;
-
- generate(b, b.mIncludePad, b.mIncludePad);
-
- Builder.recycle(b);
+ this(Builder.obtain(source, bufstart, bufend, paint, outerwidth)
+ .setAlignment(align)
+ .setTextDirection(textDir)
+ .setLineSpacing(spacingadd, spacingmult)
+ .setIncludePad(includepad)
+ .setEllipsize(ellipsize)
+ .setEllipsizedWidth(ellipsizedWidth)
+ .setMaxLines(maxLines), includepad,
+ ellipsize != null ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
}
- /**
- * Used by DynamicLayout.
- */
- /* package */ StaticLayout(@Nullable CharSequence text) {
- super(text, null, 0, null, 0, 0);
+ private StaticLayout(Builder b, boolean trackPadding, int columnSize) {
+ super((b.mEllipsize == null) ? b.mText : (b.mText instanceof Spanned)
+ ? new SpannedEllipsizer(b.mText) : new Ellipsizer(b.mText),
+ b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
+ b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
+ b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
+ b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig);
- mColumns = COLUMNS_ELLIPSIZE;
- mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
- mLines = ArrayUtils.newUnpaddedIntArray(2 * mColumns);
- }
-
- private StaticLayout(Builder b) {
- super((b.mEllipsize == null)
- ? b.mText
- : (b.mText instanceof Spanned)
- ? new SpannedEllipsizer(b.mText)
- : new Ellipsizer(b.mText),
- b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
-
+ mColumns = columnSize;
if (b.mEllipsize != null) {
Ellipsizer e = (Ellipsizer) getText();
e.mLayout = this;
e.mWidth = b.mEllipsizedWidth;
e.mMethod = b.mEllipsize;
- mEllipsizedWidth = b.mEllipsizedWidth;
-
- mColumns = COLUMNS_ELLIPSIZE;
- } else {
- mColumns = COLUMNS_NORMAL;
- mEllipsizedWidth = b.mWidth;
}
mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
@@ -605,9 +559,8 @@
mLeftIndents = b.mLeftIndents;
mRightIndents = b.mRightIndents;
- setJustificationMode(b.mJustificationMode);
- generate(b, b.mIncludePad, b.mIncludePad);
+ generate(b, b.mIncludePad, trackPadding);
}
private static int getBaseHyphenationFrequency(int frequency) {
@@ -648,7 +601,7 @@
mLineCount = 0;
mEllipsized = false;
mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT;
- mFallbackLineSpacing = b.mFallbackLineSpacing;
+ boolean isFallbackLineSpacing = b.mFallbackLineSpacing;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
@@ -887,17 +840,17 @@
boolean moreChars = (endPos < bufEnd);
- final int ascent = mFallbackLineSpacing
+ final int ascent = isFallbackLineSpacing
? Math.min(fmAscent, Math.round(ascents[breakIndex]))
: fmAscent;
- final int descent = mFallbackLineSpacing
+ final int descent = isFallbackLineSpacing
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
// The fallback ascent/descent may be larger than top/bottom of the default font
// metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
// clipping.
- if (mFallbackLineSpacing) {
+ if (isFallbackLineSpacing) {
if (ascent < fmTop) {
fmTop = ascent;
}
@@ -1410,16 +1363,6 @@
return mLines[mColumns * line + ELLIPSIS_START];
}
- @Override
- public int getEllipsizedWidth() {
- return mEllipsizedWidth;
- }
-
- @Override
- public boolean isFallbackLineSpacingEnabled() {
- return mFallbackLineSpacing;
- }
-
/**
* Return the total height of this layout.
*
@@ -1445,8 +1388,6 @@
private int mTopPadding, mBottomPadding;
@UnsupportedAppUsage
private int mColumns;
- private int mEllipsizedWidth;
- private boolean mFallbackLineSpacing;
/**
* Keeps track if ellipsize is applied to the text.
diff --git a/core/java/android/text/TEST_MAPPING b/core/java/android/text/TEST_MAPPING
new file mode 100644
index 0000000..0fe974a
--- /dev/null
+++ b/core/java/android/text/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c11f497..e673676 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -47,6 +47,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.gui.DropInputMode;
+import android.gui.StalledTransactionInfo;
import android.hardware.DataSpace;
import android.hardware.HardwareBuffer;
import android.hardware.OverlayProperties;
@@ -292,6 +293,7 @@
long nativeObject, long nativeTpc, TrustedPresentationThresholds thresholds);
private static native void nativeClearTrustedPresentationCallback(long transactionObj,
long nativeObject);
+ private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid);
/**
* Transforms that can be applied to buffers as they are displayed to a window.
@@ -4363,4 +4365,11 @@
callback.accept(fence);
}
+ /**
+ * @hide
+ */
+ public static StalledTransactionInfo getStalledTransactionInfo(int pid) {
+ return nativeGetStalledTransactionInfo(pid);
+ }
+
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 4464d19..d31f823 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManagerGlobal;
+import android.os.IInputConstants;
import android.util.ArrayMap;
import android.util.Pools.SynchronizedPool;
@@ -53,11 +54,13 @@
public @interface VelocityTrackableMotionEventAxis {}
/**
- * Velocity Tracker Strategy: Invalid.
+ * Use the default Velocity Tracker Strategy. Different axes may use different default
+ * strategies.
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+ public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_DEFAULT;
/**
* Velocity Tracker Strategy: Impulse.
@@ -66,7 +69,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+ public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_IMPULSE;
/**
* Velocity Tracker Strategy: LSQ1.
@@ -77,7 +81,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+ public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ1;
/**
* Velocity Tracker Strategy: LSQ2.
@@ -88,7 +93,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+ public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ2;
/**
* Velocity Tracker Strategy: LSQ3.
@@ -98,7 +104,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+ public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ3;
/**
* Velocity Tracker Strategy: WLSQ2_DELTA.
@@ -106,7 +113,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+ public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA;
/**
* Velocity Tracker Strategy: WLSQ2_CENTRAL.
@@ -114,7 +122,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+ public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL;
/**
* Velocity Tracker Strategy: WLSQ2_RECENT.
@@ -122,7 +131,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+ public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT;
/**
* Velocity Tracker Strategy: INT1.
@@ -134,7 +144,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+ public static final int VELOCITY_TRACKER_STRATEGY_INT1 =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_INT1;
/**
* Velocity Tracker Strategy: INT2.
@@ -144,7 +155,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+ public static final int VELOCITY_TRACKER_STRATEGY_INT2 =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_INT2;
/**
* Velocity Tracker Strategy: Legacy.
@@ -155,7 +167,8 @@
*
* @hide
*/
- public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
+ public static final int VELOCITY_TRACKER_STRATEGY_LEGACY =
+ IInputConstants.VELOCITY_TRACKER_STRATEGY_LEGACY;
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e0e8a6b..92509c9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16212,7 +16212,27 @@
if (fg != null && isVisible != fg.isVisible()) {
fg.setVisible(isVisible, false);
}
+ notifyAutofillManagerViewVisibilityChanged(isVisible);
+ if (isVisible != oldVisible) {
+ if (isAccessibilityPane()) {
+ notifyViewAccessibilityStateChangedIfNeeded(isVisible
+ ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
+ : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
+ }
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
+
+ if (!getSystemGestureExclusionRects().isEmpty()) {
+ postUpdate(this::updateSystemGestureExclusionRects);
+ }
+
+ if (!collectPreferKeepClearRects().isEmpty()) {
+ postUpdate(this::updateKeepClearRects);
+ }
+ }
+ }
+
+ private void notifyAutofillManagerViewVisibilityChanged(boolean isVisible) {
if (isAutofillable()) {
AutofillManager afm = getAutofillManager();
@@ -16236,24 +16256,6 @@
}
}
}
-
- if (isVisible != oldVisible) {
- if (isAccessibilityPane()) {
- notifyViewAccessibilityStateChangedIfNeeded(isVisible
- ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
- : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
- }
-
- notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
-
- if (!getSystemGestureExclusionRects().isEmpty()) {
- postUpdate(this::updateSystemGestureExclusionRects);
- }
-
- if (!collectPreferKeepClearRects().isEmpty()) {
- postUpdate(this::updateKeepClearRects);
- }
- }
}
/**
@@ -22128,6 +22130,8 @@
// Invoking onVisibilityAggregated directly here since the subtree
// will also receive detached from window
onVisibilityAggregated(false);
+ } else {
+ notifyAutofillManagerViewVisibilityChanged(false);
}
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5d1a81f..bb1d5b1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1302,7 +1302,7 @@
* ratio or orientation specified in the app manifest.
*
* <p>The aspect ratio compatibility override is exposed to users in device
- * settings. A menu in device settings lists all apps that don't opt out of
+ * settings. A menu in device settings lists all apps that have not opted out of
* the compatibility override. Users select apps from the menu and set the
* app aspect ratio on a per-app basis. Typically, the menu is available
* only on large screen devices.
@@ -1347,11 +1347,11 @@
* Application level
* {@link android.content.pm.PackageManager.Property PackageManager.Property}
* tag that (when set to false) informs the system the app has opted out of the
- * full-screen option of the aspect ratio compatibility override. (For
- * background information about the aspect ratio compatibility override, see
+ * full-screen option of the user aspect ratio compatibility override settings. (For
+ * background information about the user aspect ratio compatibility override, see
* {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE}.)
*
- * <p>When users apply the aspect ratio compatibility override, the orientation
+ * <p>When users apply the full-screen compatibility override, the orientation
* of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}.
*
* <p>The user override is intended to improve the app experience on devices
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index d588c48..f67a61b 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -160,6 +160,8 @@
@NonNull
private final AtomicReference<InputConnection> mInputConnectionRef;
+ @NonNull
+ private final AtomicBoolean mDeactivateRequested = new AtomicBoolean(false);
@NonNull
private final Looper mLooper;
@@ -211,10 +213,6 @@
return mInputConnectionRef.get() == null;
}
- private boolean isActive() {
- return mParentInputMethodManager.isActive() && !isFinished();
- }
-
private View getServedView() {
return mServedView.get();
}
@@ -349,25 +347,15 @@
*/
@Dispatching(cancellable = false)
public void deactivate() {
- if (isFinished()) {
+ if (mDeactivateRequested.getAndSet(true)) {
// This is a small performance optimization. Still only the 1st call of
- // reportFinish() will take effect.
+ // deactivate() will take effect.
return;
}
dispatch(() -> {
- // Note that we do not need to worry about race condition here, because 1) mFinished is
- // updated only inside this block, and 2) the code here is running on a Handler hence we
- // assume multiple closeConnection() tasks will not be handled at the same time.
- if (isFinished()) {
- return;
- }
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
try {
InputConnection ic = getInputConnection();
- // Note we do NOT check isActive() here, because this is safe
- // for an IME to call at any time, and we need to allow it
- // through to clean up our state after the IME has switched to
- // another client.
if (ic == null) {
return;
}
@@ -429,7 +417,7 @@
public String toString() {
return "RemoteInputConnectionImpl{"
+ "connection=" + getInputConnection()
- + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ + " mDeactivateRequested=" + mDeactivateRequested.get()
+ " mServedView=" + mServedView.get()
+ "}";
}
@@ -464,7 +452,7 @@
public void dispatchReportFullscreenMode(boolean enabled) {
dispatch(() -> {
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
return;
}
ic.reportFullscreenMode(enabled);
@@ -480,7 +468,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
return null;
}
@@ -502,7 +490,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
return null;
}
@@ -524,7 +512,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getSelectedText on inactive InputConnection");
return null;
}
@@ -546,7 +534,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getSurroundingText on inactive InputConnection");
return null;
}
@@ -574,7 +562,7 @@
return 0; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
return 0;
}
@@ -591,7 +579,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getExtractedText on inactive InputConnection");
return null;
}
@@ -608,7 +596,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
}
@@ -625,7 +613,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
}
@@ -641,7 +629,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitCompletion on inactive InputConnection");
return;
}
@@ -657,7 +645,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitCorrection on inactive InputConnection");
return;
}
@@ -677,7 +665,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setSelection on inactive InputConnection");
return;
}
@@ -693,7 +681,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performEditorAction on inactive InputConnection");
return;
}
@@ -709,7 +697,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performContextMenuAction on inactive InputConnection");
return;
}
@@ -725,7 +713,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
}
@@ -746,7 +734,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
}
@@ -763,7 +751,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
return;
}
@@ -780,7 +768,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
return;
}
@@ -809,7 +797,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "finishComposingTextFromImm on inactive InputConnection");
return;
}
@@ -833,7 +821,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null && !isActive()) {
+ if (ic == null && mDeactivateRequested.get()) {
Log.w(TAG, "finishComposingText on inactive InputConnection");
return;
}
@@ -849,7 +837,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "sendKeyEvent on inactive InputConnection");
return;
}
@@ -865,7 +853,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
return;
}
@@ -882,7 +870,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
return;
}
@@ -899,7 +887,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
return;
}
@@ -919,7 +907,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "beginBatchEdit on inactive InputConnection");
return;
}
@@ -935,7 +923,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "endBatchEdit on inactive InputConnection");
return;
}
@@ -951,7 +939,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performSpellCheck on inactive InputConnection");
return;
}
@@ -968,7 +956,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performPrivateCommand on inactive InputConnection");
return;
}
@@ -1006,7 +994,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performHandwritingGesture on inactive InputConnection");
if (resultReceiver != null) {
resultReceiver.send(
@@ -1046,7 +1034,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "previewHandwritingGesture on inactive InputConnection");
return; // cancelled
}
@@ -1094,7 +1082,7 @@
@InputConnection.CursorUpdateMode int cursorUpdateMode,
@InputConnection.CursorUpdateFilter int cursorUpdateFilter, int imeDisplayId) {
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "requestCursorUpdates on inactive InputConnection");
return false;
}
@@ -1131,7 +1119,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "requestTextBoundsInfo on inactive InputConnection");
resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
return;
@@ -1160,7 +1148,7 @@
return false; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitContent on inactive InputConnection");
return false;
}
@@ -1185,7 +1173,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setImeConsumesInput on inactive InputConnection");
return;
}
@@ -1209,7 +1197,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "replaceText on inactive InputConnection");
return;
}
@@ -1228,7 +1216,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
}
@@ -1248,7 +1236,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "setSelection on inactive InputConnection");
return;
}
@@ -1265,7 +1253,7 @@
return null; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getSurroundingText on inactive InputConnection");
return null;
}
@@ -1293,7 +1281,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
return;
}
@@ -1309,7 +1297,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "sendKeyEvent on inactive InputConnection");
return;
}
@@ -1325,7 +1313,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performEditorAction on inactive InputConnection");
return;
}
@@ -1341,7 +1329,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "performContextMenuAction on inactive InputConnection");
return;
}
@@ -1358,7 +1346,7 @@
return 0; // cancelled
}
final InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
return 0;
}
@@ -1374,7 +1362,7 @@
return; // cancelled
}
InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
+ if (ic == null || mDeactivateRequested.get()) {
Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
return;
}
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
index 107cac2..bc71bee 100644
--- a/core/java/android/widget/TEST_MAPPING
+++ b/core/java/android/widget/TEST_MAPPING
@@ -45,6 +45,17 @@
"exclude-annotation": "android.platform.test.annotations.AppModeFull"
}
]
+ },
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
}
]
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 0a938ef4..79152b4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -185,7 +185,7 @@
private boolean mHaveBatteryLevel;
private boolean mRecordingHistory;
- private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
+ static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
@@ -1872,6 +1872,7 @@
}
return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
} else {
+ tag.poolIdx = HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
// Tag pool overflow: include the tag itself in the parcel
return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index ccc3454..4c2b2854 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -309,7 +309,11 @@
BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag();
tag.readFromParcel(src);
tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
- mHistoryTags.put(tag.poolIdx, tag);
+ if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) {
+ mHistoryTags.put(tag.poolIdx, tag);
+ } else {
+ tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
+ }
outTag.setTo(tag);
} else {
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index ffe844d..27c4cd4 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -34,26 +34,28 @@
struct APerformanceHintSession;
typedef APerformanceHintManager* (*APH_getManager)();
+typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager);
typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
size_t, int64_t);
-typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager);
typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_closeSession)(APerformanceHintSession* session);
typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t);
typedef int (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
+typedef void (*APH_setPreferPowerEfficiency)(APerformanceHintSession*, bool);
bool gAPerformanceHintBindingInitialized = false;
APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
APH_closeSession gAPH_closeSessionFn = nullptr;
APH_sendHint gAPH_sendHintFn = nullptr;
APH_setThreads gAPH_setThreadsFn = nullptr;
APH_getThreadIds gAPH_getThreadIdsFn = nullptr;
+APH_setPreferPowerEfficiency gAPH_setPreferPowerEfficiencyFn = nullptr;
void ensureAPerformanceHintBindingInitialized() {
if (gAPerformanceHintBindingInitialized) return;
@@ -65,10 +67,6 @@
LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
"Failed to find required symbol APerformanceHint_getManager!");
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
gAPH_getPreferredUpdateRateNanosFn =
(APH_getPreferredUpdateRateNanos)dlsym(handle_,
"APerformanceHint_getPreferredUpdateRateNanos");
@@ -76,6 +74,10 @@
"Failed to find required symbol "
"APerformanceHint_getPreferredUpdateRateNanos!");
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
gAPH_updateTargetWorkDurationFn =
(APH_updateTargetWorkDuration)dlsym(handle_,
"APerformanceHint_updateTargetWorkDuration");
@@ -96,8 +98,7 @@
gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol "
- "APerformanceHint_sendHint!");
+ "Failed to find required symbol APerformanceHint_sendHint!");
gAPH_setThreadsFn = (APH_setThreads)dlsym(handle_, "APerformanceHint_setThreads");
LOG_ALWAYS_FATAL_IF(gAPH_setThreadsFn == nullptr,
@@ -107,6 +108,13 @@
LOG_ALWAYS_FATAL_IF(gAPH_getThreadIdsFn == nullptr,
"Failed to find required symbol APerformanceHint_getThreadIds!");
+ gAPH_setPreferPowerEfficiencyFn =
+ (APH_setPreferPowerEfficiency)dlsym(handle_,
+ "APerformanceHint_setPreferPowerEfficiency");
+ LOG_ALWAYS_FATAL_IF(gAPH_setPreferPowerEfficiencyFn == nullptr,
+ "Failed to find required symbol"
+ "APerformanceHint_setPreferPowerEfficiency!");
+
gAPerformanceHintBindingInitialized = true;
}
@@ -223,6 +231,13 @@
return jintArr;
}
+static void nativeSetPreferPowerEfficiency(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
+ jboolean enabled) {
+ ensureAPerformanceHintBindingInitialized();
+ gAPH_setPreferPowerEfficiencyFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
+ enabled);
+}
+
static const JNINativeMethod gPerformanceHintMethods[] = {
{"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
{"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
@@ -233,6 +248,7 @@
{"nativeSendHint", "(JI)V", (void*)nativeSendHint},
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
{"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds},
+ {"nativeSetPreferPowerEfficiency", "(JZ)V", (void*)nativeSetPreferPowerEfficiency},
};
int register_android_os_PerformanceHintManager(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4249253..dbe0338 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -257,6 +257,14 @@
jmethodID onTrustedPresentationChanged;
} gTrustedPresentationCallbackClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+ jfieldID layerName;
+ jfieldID bufferId;
+ jfieldID frameNumber;
+} gStalledTransactionInfoClassInfo;
+
constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
switch (colorMode) {
case ui::ColorMode::DISPLAY_P3:
@@ -2032,6 +2040,29 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeTpc));
}
+static jobject nativeGetStalledTransactionInfo(JNIEnv* env, jclass clazz, jint pid) {
+ std::optional<gui::StalledTransactionInfo> stalledTransactionInfo =
+ SurfaceComposerClient::getStalledTransactionInfo(pid);
+ if (!stalledTransactionInfo) {
+ return nullptr;
+ }
+
+ jobject jStalledTransactionInfo = env->NewObject(gStalledTransactionInfoClassInfo.clazz,
+ gStalledTransactionInfoClassInfo.ctor);
+ if (!jStalledTransactionInfo) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+
+ env->SetObjectField(jStalledTransactionInfo, gStalledTransactionInfoClassInfo.layerName,
+ env->NewStringUTF(String8{stalledTransactionInfo->layerName}));
+ env->SetLongField(jStalledTransactionInfo, gStalledTransactionInfoClassInfo.bufferId,
+ static_cast<jlong>(stalledTransactionInfo->bufferId));
+ env->SetLongField(jStalledTransactionInfo, gStalledTransactionInfoClassInfo.frameNumber,
+ static_cast<jlong>(stalledTransactionInfo->frameNumber));
+ return jStalledTransactionInfo;
+}
+
// ----------------------------------------------------------------------------
SurfaceControl* android_view_SurfaceControl_getNativeSurfaceControl(JNIEnv* env,
@@ -2281,6 +2312,8 @@
{"nativeCreateTpc", "(Landroid/view/SurfaceControl$TrustedPresentationCallback;)J",
(void*)nativeCreateTpc},
{"getNativeTrustedPresentationCallbackFinalizer", "()J", (void*)getNativeTrustedPresentationCallbackFinalizer },
+ {"nativeGetStalledTransactionInfo", "(I)Landroid/gui/StalledTransactionInfo;",
+ (void*) nativeGetStalledTransactionInfo },
// clang-format on
};
@@ -2524,6 +2557,18 @@
gTrustedPresentationCallbackClassInfo.onTrustedPresentationChanged =
GetMethodIDOrDie(env, trustedPresentationCallbackClazz, "onTrustedPresentationChanged",
"(Z)V");
+
+ jclass stalledTransactionInfoClazz = FindClassOrDie(env, "android/gui/StalledTransactionInfo");
+ gStalledTransactionInfoClassInfo.clazz = MakeGlobalRefOrDie(env, stalledTransactionInfoClazz);
+ gStalledTransactionInfoClassInfo.ctor =
+ GetMethodIDOrDie(env, stalledTransactionInfoClazz, "<init>", "()V");
+ gStalledTransactionInfoClassInfo.layerName =
+ GetFieldIDOrDie(env, stalledTransactionInfoClazz, "layerName", "Ljava/lang/String;");
+ gStalledTransactionInfoClassInfo.bufferId =
+ GetFieldIDOrDie(env, stalledTransactionInfoClazz, "bufferId", "J");
+ gStalledTransactionInfoClassInfo.frameNumber =
+ GetFieldIDOrDie(env, stalledTransactionInfoClazz, "frameNumber", "J");
+
return err;
}
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 05c9f68..03e9a6a 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -16,13 +16,14 @@
#define LOG_TAG "VelocityTracker-JNI"
+#include <android-base/logging.h>
#include <android_runtime/AndroidRuntime.h>
#include <cutils/properties.h>
#include <input/Input.h>
#include <input/VelocityTracker.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
+
#include "android_view_MotionEvent.h"
#include "core_jni_helpers.h"
@@ -102,7 +103,7 @@
jobject eventObj) {
const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
if (!event) {
- ALOGW("nativeAddMovement failed because MotionEvent was finalized.");
+ LOG(WARNING) << "nativeAddMovement failed because MotionEvent was finalized.";
return;
}
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
index a950a79..34ccb48 100644
--- a/core/proto/android/server/windowmanagertransitiontrace.proto
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -56,6 +56,7 @@
repeated Target targets = 8;
optional int32 flags = 9;
optional int64 abort_time_ns = 10;
+ optional int64 starting_window_remove_time_ns = 11;
}
message Target {
diff --git a/core/res/res/raw/default_ringtone_vibration_effect.ahv b/core/res/res/raw/default_ringtone_vibration_effect.ahv
new file mode 100644
index 0000000..c66fc04
--- /dev/null
+++ b/core/res/res/raw/default_ringtone_vibration_effect.ahv
@@ -0,0 +1,47 @@
+<?xml version='1.0' encoding='utf-8' standalone='no' ?>
+
+<!--
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<vibration>
+ <waveform-effect>
+ <waveform-entry amplitude="0" durationMs="0" />
+ <waveform-entry amplitude="255" durationMs="12" />
+ <waveform-entry amplitude="0" durationMs="250" />
+ <waveform-entry amplitude="255" durationMs="12" />
+ <waveform-entry amplitude="0" durationMs="500" />
+ <repeating>
+ <waveform-entry amplitude="77" durationMs="50" />
+ <waveform-entry amplitude="77" durationMs="50" />
+ <waveform-entry amplitude="78" durationMs="50" />
+ <waveform-entry amplitude="79" durationMs="50" />
+ <waveform-entry amplitude="81" durationMs="50" />
+ <waveform-entry amplitude="84" durationMs="50" />
+ <waveform-entry amplitude="87" durationMs="50" />
+ <waveform-entry amplitude="93" durationMs="50" />
+ <waveform-entry amplitude="101" durationMs="50" />
+ <waveform-entry amplitude="114" durationMs="50" />
+ <waveform-entry amplitude="133" durationMs="50" />
+ <waveform-entry amplitude="162" durationMs="50" />
+ <waveform-entry amplitude="205" durationMs="50" />
+ <waveform-entry amplitude="255" durationMs="50" />
+ <waveform-entry amplitude="255" durationMs="300" />
+ <waveform-entry amplitude="0" durationMs="1000" />
+ </repeating>
+ </waveform-effect>
+</vibration>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7ea8f8d..72333fb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5372,6 +5372,12 @@
<!-- Default value for performant auth feature. -->
<bool name="config_performantAuthDefault">false</bool>
+ <!-- Threshold for false rejection rate (FRR) of biometric authentication. Applies for both
+ fingerprint and face. If a dual-modality device only enrolled a single biometric and
+ experiences high FRR (above threshold), system notification will be sent to encourage user
+ to enroll the other eligible biometric. -->
+ <fraction name="config_biometricNotificationFrrThreshold">30%</fraction>
+
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
only. The component must be part of a system app. -->
diff --git a/core/res/res/values/config_battery_saver.xml b/core/res/res/values/config_battery_saver.xml
index eb396df..e1b0ef4 100644
--- a/core/res/res/values/config_battery_saver.xml
+++ b/core/res/res/values/config_battery_saver.xml
@@ -26,6 +26,10 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
+ <!-- Whether or not the device supports battery saver. If false, battery saver will be
+ disabled. -->
+ <bool name="config_batterySaverSupported">true</bool>
+
<!-- Whether or not battery saver should be "sticky" when manually enabled. -->
<bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d828f33..c5aa8b0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1837,6 +1837,8 @@
<string name="fingerprint_error_not_match">Fingerprint not recognized</string>
<!-- Message shown when UDFPS fails to match -->
<string name="fingerprint_udfps_error_not_match">Fingerprint not recognized</string>
+ <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
+ <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 52f4db5..851c0c2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2594,6 +2594,9 @@
<java-symbol type="string" name="biometric_error_device_not_secured" />
<java-symbol type="string" name="biometric_error_generic" />
+ <!-- Biometric FRR config -->
+ <java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+
<!-- Device credential strings for BiometricManager -->
<java-symbol type="string" name="screen_lock_app_setting_name" />
<java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
@@ -2607,6 +2610,7 @@
<java-symbol type="string" name="fingerprint_error_vendor_unknown" />
<java-symbol type="string" name="fingerprint_error_not_match" />
<java-symbol type="string" name="fingerprint_udfps_error_not_match" />
+ <java-symbol type="string" name="fingerprint_dialog_use_fingerprint_instead" />
<java-symbol type="string" name="fingerprint_acquired_partial" />
<java-symbol type="string" name="fingerprint_acquired_insufficient" />
<java-symbol type="string" name="fingerprint_acquired_imager_dirty" />
@@ -4015,6 +4019,7 @@
<!-- Battery saver config -->
<java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+ <java-symbol type="bool" name="config_batterySaverSupported" />
<java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" />
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
@@ -5189,4 +5194,6 @@
<!-- For ActivityManager PSS profiling configurability -->
<java-symbol type="bool" name="config_am_disablePssProfiling" />
+
+ <java-symbol type="raw" name="default_ringtone_vibration_effect" />
</resources>
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index b0826ab..20ba427 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -173,4 +173,13 @@
session.setThreads(new int[]{-1});
});
}
+
+ @Test
+ public void testSetPreferPowerEfficiency() {
+ Session s = createSession();
+ assumeNotNull(s);
+ s.setPreferPowerEfficiency(false);
+ s.setPreferPowerEfficiency(true);
+ s.setPreferPowerEfficiency(true);
+ }
}
diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
index 2232e3a..e1f9523 100644
--- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
+++ b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.os.ConditionVariable;
import android.os.PowerMonitor;
import android.os.PowerMonitorReadings;
@@ -29,13 +30,16 @@
import java.util.List;
public class SystemHealthManagerTest {
+ private List<PowerMonitor> mPowerMonitorInfo;
+ private PowerMonitorReadings mReadings;
+ private RuntimeException mException;
@Test
public void getPowerMonitors() {
SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
- PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors();
+ List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors();
assertThat(powerMonitorInfo).isNotNull();
- if (powerMonitorInfo.length == 0) {
+ if (powerMonitorInfo.isEmpty()) {
// This device does not support PowerStats HAL
return;
}
@@ -50,20 +54,73 @@
}
}
- List<PowerMonitor> pmis = new ArrayList<>();
+ List<PowerMonitor> selectedMonitors = new ArrayList<>();
if (consumerMonitor != null) {
- pmis.add(consumerMonitor);
+ selectedMonitors.add(consumerMonitor);
}
if (measurementMonitor != null) {
- pmis.add(measurementMonitor);
+ selectedMonitors.add(measurementMonitor);
}
- PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]);
PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors);
for (PowerMonitor monitor : selectedMonitors) {
assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0);
- assertThat(readings.getTimestampMs(monitor)).isGreaterThan(0);
+ assertThat(readings.getTimestamp(monitor)).isGreaterThan(0);
+ }
+ }
+
+ @Test
+ public void getPowerMonitorsAsync() {
+ SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
+ ConditionVariable done = new ConditionVariable();
+ shm.getSupportedPowerMonitors(null, pms -> {
+ mPowerMonitorInfo = pms;
+ done.open();
+ });
+ done.block();
+ assertThat(mPowerMonitorInfo).isNotNull();
+ if (mPowerMonitorInfo.isEmpty()) {
+ // This device does not support PowerStats HAL
+ return;
+ }
+
+ PowerMonitor consumerMonitor = null;
+ PowerMonitor measurementMonitor = null;
+ for (PowerMonitor pmi : mPowerMonitorInfo) {
+ if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
+ measurementMonitor = pmi;
+ } else {
+ consumerMonitor = pmi;
+ }
+ }
+
+ List<PowerMonitor> selectedMonitors = new ArrayList<>();
+ if (consumerMonitor != null) {
+ selectedMonitors.add(consumerMonitor);
+ }
+ if (measurementMonitor != null) {
+ selectedMonitors.add(measurementMonitor);
+ }
+
+ done.close();
+ shm.getPowerMonitorReadings(selectedMonitors, null,
+ readings -> {
+ mReadings = readings;
+ done.open();
+ },
+ exception -> {
+ mException = exception;
+ done.open();
+ }
+ );
+ done.block();
+
+ assertThat(mException).isNull();
+
+ for (PowerMonitor monitor : selectedMonitors) {
+ assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0);
+ assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0);
}
}
}
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 5fe17ee..4d446901 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.controls.actions.CommandAction;
@@ -53,6 +55,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -307,6 +310,18 @@
intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
}
+ @Test
+ public void testOnNextDoesntRethrowDeadObjectException() throws RemoteException {
+ doAnswer(invocation -> {
+ throw new DeadObjectException();
+ }).when(mSubscriber).onNext(ArgumentMatchers.any(), ArgumentMatchers.any());
+ Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+
+ sendControlGetControl(control);
+
+ assertTrue(mControlsProviderService.mSubscription.mIsCancelled);
+ }
+
/**
* Sends the control through the publisher in {@code mControlsProviderService}, returning
* the control obtained by the subscriber
@@ -359,6 +374,7 @@
}
private List<Control> mControls;
+ private FakeSubscription mSubscription;
public void setControls(List<Control> controls) {
mControls = controls;
@@ -398,17 +414,35 @@
}
private Subscription createSubscription(Subscriber s, List<Control> controls) {
- return new Subscription() {
- public void request(long n) {
- int i = 0;
- for (Control c : mControls) {
- if (i++ < n) s.onNext(c);
- else break;
- }
- s.onComplete();
- }
- public void cancel() {}
- };
+ FakeSubscription subscription = new FakeSubscription(s, controls);
+ mSubscription = subscription;
+ return subscription;
+ }
+ }
+
+ private static final class FakeSubscription implements Subscription {
+
+ private final Subscriber mSubscriber;
+ private final List<Control> mControls;
+
+ private boolean mIsCancelled = false;
+
+ FakeSubscription(Subscriber s, List<Control> controls) {
+ mSubscriber = s;
+ mControls = controls;
+ }
+
+ public void request(long n) {
+ int i = 0;
+ for (Control c : mControls) {
+ if (i++ < n) mSubscriber.onNext(c);
+ else break;
+ }
+ mSubscriber.onComplete();
+ }
+
+ public void cancel() {
+ mIsCancelled = true;
}
}
}
diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp
index 453b476..7b5d7df 100644
--- a/core/tests/packagemonitortests/Android.bp
+++ b/core/tests/packagemonitortests/Android.bp
@@ -24,6 +24,9 @@
android_test {
name: "FrameworksCorePackageMonitorTests",
srcs: ["src/**/*.java"],
+ exclude_srcs: [
+ "src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java",
+ ],
static_libs: [
"androidx.test.runner",
"compatibility-device-util-axt",
@@ -39,3 +42,19 @@
":TestVisibilityApp",
],
}
+
+android_test {
+ name: "FrameworksCorePackageMonitorWithoutPermissionTests",
+ srcs: ["src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java"],
+ manifest: "AndroidManifestNoPermission.xml",
+ static_libs: [
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "frameworks-base-testutils",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+ test_config: "AndroidTestWithoutPermission.xml",
+}
diff --git a/core/tests/packagemonitortests/AndroidManifestNoPermission.xml b/core/tests/packagemonitortests/AndroidManifestNoPermission.xml
new file mode 100644
index 0000000..5d6308a
--- /dev/null
+++ b/core/tests/packagemonitortests/AndroidManifestNoPermission.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.packagemonitor.withoutpermission">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.packagemonitor.withoutpermission"
+ android:label="Frameworks PackageMonitor Core without Permission Tests" />
+</manifest>
diff --git a/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml b/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml
new file mode 100644
index 0000000..37a6a2f
--- /dev/null
+++ b/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+
+<configuration description="Runs Frameworks Core Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksCorePackageMonitorWithoutPermissionTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksCorePackageMonitorWithoutPermissionTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.packagemonitor.withoutpermission" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
index a310edc..da8e4cc 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
@@ -20,9 +20,12 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.UserHandle;
@@ -45,6 +48,25 @@
TEST_DATA_PATH + "TestVisibilityApp.apk";
private static final String TEAT_APK_PACKAGE_NAME = "com.example.android.testvisibilityapp";
private static final int WAIT_CALLBACK_CALLED_IN_SECONDS = 1;
+
+ @Test
+ public void testPackageMonitorCallbackMultipleRegisterThrowsException() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(android.os.Bundle bundle) {
+ // do-nothing
+ }
+ };
+ try {
+ context.getPackageManager().registerPackageMonitorCallback(callback, 0);
+ assertThrows(IllegalStateException.class,
+ () -> context.getPackageManager().registerPackageMonitorCallback(callback, 0));
+ } finally {
+ context.getPackageManager().unregisterPackageMonitorCallback(callback);
+ }
+ }
+
@Test
public void testPackageMonitorPackageVisible() throws Exception {
TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor();
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java
new file mode 100644
index 0000000..659c0c2
--- /dev/null
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.internal.content.withoutpermission;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.content.PackageMonitor;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * A test to verify PackageMonitor implementation without INTERACT_ACROSS_USERS_FULL permission.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PackageMonitorPermissionTest {
+
+ @Test
+ public void testPackageMonitorNoCrossUserPermission() throws Exception {
+ TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor();
+
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ assertThrows(SecurityException.class,
+ () -> testPackageMonitor.register(context, UserHandle.ALL,
+ new Handler(Looper.getMainLooper())));
+ }
+
+ private static class TestVisibilityPackageMonitor extends PackageMonitor {
+ }
+}
diff --git a/graphics/TEST_MAPPING b/graphics/TEST_MAPPING
index abeaf19..8afc30d 100644
--- a/graphics/TEST_MAPPING
+++ b/graphics/TEST_MAPPING
@@ -7,6 +7,18 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ],
+ "file_patterns": ["(/|^)Typeface\\.java", "(/|^)Paint\\.java"]
}
]
}
diff --git a/graphics/java/android/graphics/TEST_MAPPING b/graphics/java/android/graphics/TEST_MAPPING
new file mode 100644
index 0000000..df91222
--- /dev/null
+++ b/graphics/java/android/graphics/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ],
+ "file_patterns": [
+ "Typeface\\.java",
+ "Paint\\.java",
+ "[^/]*Canvas\\.java",
+ "[^/]*Font[^/]*\\.java"
+ ]
+ }
+ ]
+}
diff --git a/graphics/java/android/graphics/fonts/TEST_MAPPING b/graphics/java/android/graphics/fonts/TEST_MAPPING
new file mode 100644
index 0000000..99cbfe7
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/graphics/java/android/graphics/text/TEST_MAPPING b/graphics/java/android/graphics/text/TEST_MAPPING
new file mode 100644
index 0000000..99cbfe7
--- /dev/null
+++ b/graphics/java/android/graphics/text/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsTextTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 25f5dec..b4d8def 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -36,6 +36,7 @@
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.WrappedKeyEntry;
import android.system.keystore2.AuthenticatorSpec;
+import android.system.keystore2.Authorization;
import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
@@ -966,6 +967,32 @@
authenticatorSpecs.add(authSpec);
}
+ if (parts.length > 2) {
+ @KeyProperties.EncryptionPaddingEnum int padding =
+ KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
+ if (padding == KeymasterDefs.KM_PAD_RSA_OAEP
+ && response.metadata != null
+ && response.metadata.authorizations != null) {
+ Authorization[] keyCharacteristics = response.metadata.authorizations;
+
+ for (Authorization authorization : keyCharacteristics) {
+ // Add default MGF1 digest SHA-1
+ // when wrapping key has KM_TAG_RSA_OAEP_MGF_DIGEST tag
+ if (authorization.keyParameter.tag
+ == KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST) {
+ // Default MGF1 digest is SHA-1
+ // and KeyMint only supports default MGF1 digest crypto operations
+ // for importWrappedKey.
+ args.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+ ));
+ break;
+ }
+ }
+ }
+ }
+
try {
securityLevel.importWrappedKey(
wrappedKey, wrappingkey,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 079cfa3..81384ca 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -242,9 +242,20 @@
return false;
}
+ // Abort if no space to split.
+ final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+ task.getTaskProperties(), splitPinRule,
+ splitPinRule.getDefaultSplitAttributes(),
+ getActivitiesMinDimensionsPair(primaryContainer.getTopNonFinishingActivity(),
+ topContainer.getTopNonFinishingActivity()));
+ if (!SplitPresenter.shouldShowSplit(calculatedSplitAttributes)) {
+ Log.w(TAG, "No space to split, abort pinning top ActivityStack.");
+ return false;
+ }
+
// Registers a Split
final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer,
- topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes());
+ topContainer, splitPinRule, calculatedSplitAttributes);
task.addSplitContainer(splitPinContainer);
// Updates the Split
@@ -263,7 +274,33 @@
@Override
public void unpinTopActivityStack(int taskId){
- // TODO
+ synchronized (mLock) {
+ final TaskContainer task = getTaskContainer(taskId);
+ if (task == null) {
+ Log.e(TAG, "Cannot find the task to unpin, id: " + taskId);
+ return;
+ }
+
+ final SplitPinContainer splitPinContainer = task.getSplitPinContainer();
+ if (splitPinContainer == null) {
+ Log.e(TAG, "No ActivityStack is pinned.");
+ return;
+ }
+
+ // Remove the SplitPinContainer from the task.
+ final TaskFragmentContainer containerToUnpin =
+ splitPinContainer.getSecondaryContainer();
+ task.removeSplitPinContainer();
+
+ // Resets the isolated navigation and updates the container.
+ final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
+ final WindowContainerTransaction wct = transactionRecord.getTransaction();
+ mPresenter.setTaskFragmentIsolatedNavigation(wct,
+ containerToUnpin.getTaskFragmentToken(), false /* isolated */);
+ updateContainer(wct, containerToUnpin);
+ transactionRecord.apply(false /* shouldApplyIndependently */);
+ updateCallbackIfNecessary();
+ }
}
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 16d8cb4..9a0769a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -265,8 +265,25 @@
}
void removeSplitPinContainer() {
+ if (mSplitPinContainer == null) {
+ return;
+ }
+
+ final TaskFragmentContainer primaryContainer = mSplitPinContainer.getPrimaryContainer();
+ final TaskFragmentContainer secondaryContainer = mSplitPinContainer.getSecondaryContainer();
mSplitContainers.remove(mSplitPinContainer);
mSplitPinContainer = null;
+
+ // Remove the other SplitContainers that contains the unpinned container (unless it
+ // is the current top-most split-pair), since the state are no longer valid.
+ final List<SplitContainer> splitsToRemove = new ArrayList<>();
+ for (SplitContainer splitContainer : mSplitContainers) {
+ if (splitContainer.getSecondaryContainer().equals(secondaryContainer)
+ && !splitContainer.getPrimaryContainer().equals(primaryContainer)) {
+ splitsToRemove.add(splitContainer);
+ }
+ }
+ removeSplitContainers(splitsToRemove);
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index 9181281..114486e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -379,7 +379,15 @@
@Override
public void onBackCancelled() {
- mProgressAnimator.onBackCancelled(CrossActivityAnimation.this::finishAnimation);
+ mProgressAnimator.onBackCancelled(() -> {
+ // mProgressAnimator can reach finish stage earlier than mLeavingProgressSpring,
+ // and if we release all animation leash first, the leavingProgressSpring won't
+ // able to update the animation anymore, which cause flicker.
+ // Here should force update the closing animation target to the final stage before
+ // release it.
+ setLeavingProgress(0);
+ finishAnimation();
+ });
}
@Override
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 66b9ade6..8400dde 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
@@ -25,7 +25,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
-import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_BLOCKED;
@@ -37,6 +36,7 @@
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
import android.annotation.BinderThread;
@@ -85,6 +85,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.R;
@@ -1008,9 +1009,7 @@
}
private void onNotificationPanelExpandedChanged(boolean expanded) {
- if (DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "onNotificationPanelExpandedChanged: expanded=" + expanded);
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "onNotificationPanelExpandedChanged: expanded=%b", expanded);
if (mStackView != null && mStackView.isExpanded()) {
if (expanded) {
mStackView.stopMonitoringSwipeUpGesture();
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 dce6b56..250e010 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
@@ -47,7 +47,6 @@
static final boolean DEBUG_USER_EDUCATION = false;
static final boolean DEBUG_POSITIONER = false;
public static final boolean DEBUG_COLLAPSE_ANIMATOR = false;
- static final boolean DEBUG_BUBBLE_GESTURE = false;
public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false;
private static final boolean FORCE_SHOW_USER_EDUCATION = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index f58b121..da5974f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -21,11 +21,11 @@
import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
-import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,6 +75,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
@@ -2024,9 +2025,7 @@
* Monitor for swipe up gesture that is used to collapse expanded view
*/
void startMonitoringSwipeUpGesture() {
- if (DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "startMonitoringSwipeUpGesture");
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "startMonitoringSwipeUpGesture");
stopMonitoringSwipeUpGestureInternal();
if (isGestureNavEnabled()) {
@@ -2046,9 +2045,7 @@
* Stop monitoring for swipe up gesture
*/
void stopMonitoringSwipeUpGesture() {
- if (DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "stopMonitoringSwipeUpGesture");
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "stopMonitoringSwipeUpGesture");
stopMonitoringSwipeUpGestureInternal();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
index 3a3a378..1375684 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
@@ -18,10 +18,10 @@
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.content.Context;
import android.hardware.input.InputManager;
-import android.util.Log;
import android.view.Choreographer;
import android.view.InputChannel;
import android.view.InputEventReceiver;
@@ -29,6 +29,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
/**
@@ -58,9 +59,7 @@
* @param listener listener that is notified of touch events
*/
void start(MotionEventListener listener) {
- if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "start monitoring bubbles swipe up gesture");
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "start monitoring bubbles swipe up gesture");
stopInternal();
@@ -76,9 +75,7 @@
}
void stop() {
- if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "stop monitoring bubbles swipe up gesture");
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "stop monitoring bubbles swipe up gesture");
stopInternal();
}
@@ -94,9 +91,7 @@
}
private void onInterceptTouch() {
- if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "intercept touch event");
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "intercept touch event");
if (mInputMonitor != null) {
mInputMonitor.pilferPointers();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
index 844526c..b7107f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
@@ -16,19 +16,20 @@
package com.android.wm.shell.bubbles;
-import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.content.Context;
import android.graphics.PointF;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
+import com.android.internal.protolog.common.ProtoLog;
+
/**
* Handles {@link MotionEvent}s for bubbles that begin in the nav bar area
*/
@@ -112,10 +113,8 @@
private boolean isInGestureRegion(MotionEvent ev) {
// Only handles touch events beginning in navigation bar system gesture zone
if (mPositioner.getNavBarGestureZone().contains((int) ev.getX(), (int) ev.getY())) {
- if (DEBUG_BUBBLE_GESTURE) {
- Log.d(TAG, "handling touch y=" + ev.getY()
- + " navBarGestureZone=" + mPositioner.getNavBarGestureZone());
- }
+ ProtoLog.d(WM_SHELL_BUBBLES, "handling touch x=%d y=%d navBarGestureZone=%s",
+ (int) ev.getX(), (int) ev.getY(), mPositioner.getNavBarGestureZone());
return true;
}
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
new file mode 100644
index 0000000..a141ff9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common.pip
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.PackageManager
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.pip.PipUtils
+
+class PipAppOpsListener(
+ private val mContext: Context,
+ private val mCallback: Callback,
+ private val mMainExecutor: ShellExecutor
+) {
+ private val mAppOpsManager: AppOpsManager = checkNotNull(
+ mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager)
+ private val mAppOpsChangedListener = AppOpsManager.OnOpChangedListener { _, packageName ->
+ try {
+ // Dismiss the PiP once the user disables the app ops setting for that package
+ val topPipActivityInfo = PipUtils.getTopPipActivity(mContext)
+ val componentName = topPipActivityInfo.first ?: return@OnOpChangedListener
+ val userId = topPipActivityInfo.second
+ val appInfo = mContext.packageManager
+ .getApplicationInfoAsUser(packageName, 0, userId)
+ if (appInfo.packageName == componentName.packageName &&
+ mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_PICTURE_IN_PICTURE, appInfo.uid,
+ packageName
+ ) != AppOpsManager.MODE_ALLOWED
+ ) {
+ mMainExecutor.execute { mCallback.dismissPip() }
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Unregister the listener if the package can't be found
+ unregisterAppOpsListener()
+ }
+ }
+
+ fun onActivityPinned(packageName: String) {
+ // Register for changes to the app ops setting for this package while it is in PiP
+ registerAppOpsListener(packageName)
+ }
+
+ fun onActivityUnpinned() {
+ // Unregister for changes to the previously PiP'ed package
+ unregisterAppOpsListener()
+ }
+
+ private fun registerAppOpsListener(packageName: String) {
+ mAppOpsManager.startWatchingMode(
+ AppOpsManager.OP_PICTURE_IN_PICTURE, packageName,
+ mAppOpsChangedListener
+ )
+ }
+
+ private fun unregisterAppOpsListener() {
+ mAppOpsManager.stopWatchingMode(mAppOpsChangedListener)
+ }
+
+ /** Callback for PipAppOpsListener to request changes to the PIP window. */
+ interface Callback {
+ /** Dismisses the PIP window. */
+ fun dismissPip()
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 62b0799..0998e71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -342,6 +342,7 @@
if (!mActiveLetterboxEduLayout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(mActiveLetterboxEduLayout.getDisplayId()))) {
// The layout is no longer eligible to be shown, clear active layout.
+ mActiveLetterboxEduLayout.release();
mActiveLetterboxEduLayout = null;
}
return;
@@ -371,15 +372,9 @@
ShellTaskOrganizer.TaskListener taskListener) {
return new LetterboxEduWindowManager(context, taskInfo,
mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
- mTransitionsLazy.get(), this::onLetterboxEduDismissed, mDockStateReader,
- mCompatUIConfiguration);
- }
-
- private void onLetterboxEduDismissed(
- Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
- mActiveLetterboxEduLayout = null;
- // We need to update the UI
- createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second);
+ mTransitionsLazy.get(),
+ stateInfo -> createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second),
+ mDockStateReader, mCompatUIConfiguration);
}
private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
@@ -448,6 +443,7 @@
if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) {
// The layout is no longer eligible to be shown, remove from active layouts.
+ mActiveReachabilityEduLayout.release();
mActiveReachabilityEduLayout = null;
}
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 430fa95..c06b22c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -45,6 +45,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
+import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -74,7 +75,6 @@
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
@@ -123,6 +123,12 @@
@WMSingleton
@Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
+ @WMSingleton
+ @Provides
static DisplayController provideDisplayController(Context context,
IWindowManager wmService,
ShellInit shellInit,
@@ -807,7 +813,6 @@
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 54be901..9bf973f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -31,13 +31,13 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
index f29b3a3..e8fae24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
@@ -21,7 +21,6 @@
import android.os.Handler;
import com.android.internal.logging.UiEventLogger;
-import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipMediaController;
@@ -37,12 +36,6 @@
*/
@Module
public abstract class Pip1SharedModule {
- @WMSingleton
- @Provides
- static FloatingContentCoordinator provideFloatingContentCoordinator() {
- return new FloatingContentCoordinator();
- }
-
// Needs handler for registering broadcast receivers
@WMSingleton
@Provides
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 52c6d20..80ffbb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -29,12 +29,12 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
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 d8ce427..1d46e75 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
@@ -275,7 +275,7 @@
*/
fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
val wct = WindowContainerTransaction()
- wct.setBounds(taskInfo.token, null)
+ wct.setBounds(taskInfo.token, Rect())
shellTaskOrganizer.applyTransaction(wct)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java
deleted file mode 100644
index 48a3fc2..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
-
-import android.app.AppOpsManager;
-import android.app.AppOpsManager.OnOpChangedListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.util.Pair;
-
-import com.android.wm.shell.common.ShellExecutor;
-
-public class PipAppOpsListener {
- private static final String TAG = PipAppOpsListener.class.getSimpleName();
-
- private Context mContext;
- private ShellExecutor mMainExecutor;
- private AppOpsManager mAppOpsManager;
- private Callback mCallback;
-
- private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
- @Override
- public void onOpChanged(String op, String packageName) {
- try {
- // Dismiss the PiP once the user disables the app ops setting for that package
- final Pair<ComponentName, Integer> topPipActivityInfo =
- PipUtils.getTopPipActivity(mContext);
- if (topPipActivityInfo.first != null) {
- final ApplicationInfo appInfo = mContext.getPackageManager()
- .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
- if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
- mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
- packageName) != MODE_ALLOWED) {
- mMainExecutor.execute(() -> mCallback.dismissPip());
- }
- }
- } catch (NameNotFoundException e) {
- // Unregister the listener if the package can't be found
- unregisterAppOpsListener();
- }
- }
- };
-
- public PipAppOpsListener(Context context, Callback callback, ShellExecutor mainExecutor) {
- mContext = context;
- mMainExecutor = mainExecutor;
- mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mCallback = callback;
- }
-
- public void onActivityPinned(String packageName) {
- // Register for changes to the app ops setting for this package while it is in PiP
- registerAppOpsListener(packageName);
- }
-
- public void onActivityUnpinned() {
- // Unregister for changes to the previously PiP'ed package
- unregisterAppOpsListener();
- }
-
- private void registerAppOpsListener(String packageName) {
- mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName,
- mAppOpsChangedListener);
- }
-
- private void unregisterAppOpsListener() {
- mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
- }
-
- /** Callback for PipAppOpsListener to request changes to the PIP window. */
- public interface Callback {
- /** Dismisses the PIP window. */
- void dismissPip();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index f396f3f..b872267 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -75,6 +75,7 @@
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.IPip;
@@ -82,7 +83,6 @@
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 43d3f36..b251f6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -41,7 +41,7 @@
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
-import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 2482acf..e3544c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -47,10 +47,10 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b2a189b..ee55211 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -62,13 +62,16 @@
private SurfaceControl mLeash;
private TvPipMenuView mPipMenuView;
private TvPipBackgroundView mPipBackgroundView;
- private boolean mMenuIsFocused;
@TvPipMenuMode
private int mCurrentMenuMode = MODE_NO_MENU;
@TvPipMenuMode
private int mPrevMenuMode = MODE_NO_MENU;
+ /** When the window gains focus, enter this menu mode */
+ @TvPipMenuMode
+ private int mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+
@IntDef(prefix = { "MODE_" }, value = {
MODE_NO_MENU,
MODE_MOVE_MENU,
@@ -170,6 +173,9 @@
mPipMenuView = createTvPipMenuView();
setUpViewSurfaceZOrder(mPipMenuView, 1);
addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
+ mPipMenuView.getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> {
+ onPipWindowFocusChanged(hasFocus);
+ });
}
@VisibleForTesting
@@ -224,13 +230,14 @@
void showMovementMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showMovementMenu()", TAG);
- switchToMenuMode(MODE_MOVE_MENU);
+ requestMenuMode(MODE_MOVE_MENU);
}
@Override
public void showMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
- switchToMenuMode(MODE_ALL_ACTIONS_MENU, true);
+ mPipMenuView.resetMenu();
+ requestMenuMode(MODE_ALL_ACTIONS_MENU);
}
void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
@@ -250,7 +257,7 @@
void closeMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: closeMenu()", TAG);
- switchToMenuMode(MODE_NO_MENU);
+ requestMenuMode(MODE_NO_MENU);
}
@Override
@@ -392,11 +399,15 @@
}
}
- // Start methods handling {@link TvPipMenuMode}
+ // Beginning of convenience methods for {@link TvPipMenuMode}
@VisibleForTesting
boolean isMenuOpen() {
- return mCurrentMenuMode != MODE_NO_MENU;
+ return isMenuOpen(mCurrentMenuMode);
+ }
+
+ private static boolean isMenuOpen(@TvPipMenuMode int menuMode) {
+ return menuMode != MODE_NO_MENU;
}
@VisibleForTesting
@@ -409,46 +420,6 @@
return mCurrentMenuMode == MODE_ALL_ACTIONS_MENU;
}
- private void switchToMenuMode(@TvPipMenuMode int menuMode) {
- switchToMenuMode(menuMode, false);
- }
-
- private void switchToMenuMode(@TvPipMenuMode int menuMode, boolean resetMenu) {
- // Note: we intentionally don't return early here, because the TvPipMenuView needs to
- // refresh the Ui even if there is no menu mode change.
- mPrevMenuMode = mCurrentMenuMode;
- mCurrentMenuMode = menuMode;
-
- ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: switchToMenuMode: setting mCurrentMenuMode=%s, mPrevMenuMode=%s", TAG,
- getMenuModeString(), getMenuModeString(mPrevMenuMode));
-
- updateUiOnNewMenuModeRequest(resetMenu);
- updateDelegateOnNewMenuModeRequest();
- }
-
- private void updateUiOnNewMenuModeRequest(boolean resetMenu) {
- if (mPipMenuView == null || mPipBackgroundView == null) return;
-
- mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity());
- mPipMenuView.transitionToMenuMode(mCurrentMenuMode, resetMenu);
- mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode);
- grantPipMenuFocus(mCurrentMenuMode != MODE_NO_MENU);
- }
-
- private void updateDelegateOnNewMenuModeRequest() {
- if (mPrevMenuMode == mCurrentMenuMode) return;
- if (mDelegate == null) return;
-
- if (mPrevMenuMode == MODE_MOVE_MENU || isInMoveMode()) {
- mDelegate.onInMoveModeChanged();
- }
-
- if (mCurrentMenuMode == MODE_NO_MENU) {
- mDelegate.onMenuClosed();
- }
- }
-
@VisibleForTesting
String getMenuModeString() {
return getMenuModeString(mCurrentMenuMode);
@@ -467,6 +438,90 @@
}
}
+ // Beginning of methods handling switching between menu modes
+
+ private void requestMenuMode(@TvPipMenuMode int menuMode) {
+ if (isMenuOpen() == isMenuOpen(menuMode)) {
+ // No need to request a focus change. We can directly switch to the new mode.
+ switchToMenuMode(menuMode);
+ } else {
+ if (isMenuOpen(menuMode)) {
+ mMenuModeOnFocus = menuMode;
+ }
+
+ // Send a request to gain window focus if the menu is open, or lose window focus
+ // otherwise. Once the focus change happens, we will request the new mode in the
+ // callback {@link #onPipWindowFocusChanged}.
+ requestPipMenuFocus(isMenuOpen(menuMode));
+ }
+ // Note: we don't handle cases where there is a focus change currently in flight, because
+ // this is very unlikely to happen in practice and would complicate the logic.
+ }
+
+ private void requestPipMenuFocus(boolean focus) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: requestPipMenuFocus(%b)", TAG, focus);
+
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mSystemWindows.getFocusGrantToken(mPipMenuView), focus);
+ } catch (Exception e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Unable to update focus, %s", TAG, e);
+ }
+ }
+
+ /**
+ * Called when the menu window gains or loses focus.
+ */
+ @VisibleForTesting
+ void onPipWindowFocusChanged(boolean focused) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
+ switchToMenuMode(focused ? mMenuModeOnFocus : MODE_NO_MENU);
+
+ // Reset the default menu mode for focused state.
+ mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+ }
+
+ /**
+ * Immediately switches to the menu mode in the given request. Updates the mDelegate and the UI.
+ * Doesn't handle any focus changes.
+ */
+ private void switchToMenuMode(@TvPipMenuMode int menuMode) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: switchToMenuMode: from=%s, to=%s", TAG, getMenuModeString(),
+ getMenuModeString(menuMode));
+
+ if (mCurrentMenuMode == menuMode) return;
+
+ mPrevMenuMode = mCurrentMenuMode;
+ mCurrentMenuMode = menuMode;
+ updateUiOnNewMenuModeRequest();
+ updateDelegateOnNewMenuModeRequest();
+ }
+
+ private void updateUiOnNewMenuModeRequest() {
+ if (mPipMenuView == null || mPipBackgroundView == null) return;
+
+ mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity());
+ mPipMenuView.transitionToMenuMode(mCurrentMenuMode);
+ mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode);
+ }
+
+ private void updateDelegateOnNewMenuModeRequest() {
+ if (mPrevMenuMode == mCurrentMenuMode) return;
+ if (mDelegate == null) return;
+
+ if (mPrevMenuMode == MODE_MOVE_MENU || isInMoveMode()) {
+ mDelegate.onInMoveModeChanged();
+ }
+
+ if (!isMenuOpen()) {
+ mDelegate.onMenuClosed();
+ }
+ }
+
// Start {@link TvPipMenuView.Delegate} methods
@Override
@@ -476,42 +531,19 @@
}
@Override
- public void onBackPress() {
- if (!onExitMoveMode()) {
- closeMenu();
- }
- }
-
- @Override
- public boolean onExitMoveMode() {
+ public void onExitCurrentMenuMode() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExitMoveMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
-
- final int saveMenuMode = mCurrentMenuMode;
- if (isInMoveMode()) {
- switchToMenuMode(mPrevMenuMode);
- }
- return saveMenuMode == MODE_MOVE_MENU;
+ "%s: onExitCurrentMenuMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
+ requestMenuMode(isInMoveMode() ? mPrevMenuMode : MODE_NO_MENU);
}
@Override
- public boolean onPipMovement(int keycode) {
+ public void onPipMovement(int keycode) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onPipMovement - mCurrentMenuMode=%s", TAG, getMenuModeString());
if (isInMoveMode()) {
mDelegate.movePip(keycode);
}
- return isInMoveMode();
- }
-
- @Override
- public void onPipWindowFocusChanged(boolean focused) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
- mMenuIsFocused = focused;
- if (!focused && isMenuOpen()) {
- closeMenu();
- }
}
interface Delegate {
@@ -524,21 +556,6 @@
void closeEduText();
}
- private void grantPipMenuFocus(boolean grantFocus) {
- if (mMenuIsFocused == grantFocus) return;
-
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: grantWindowFocus(%b)", TAG, grantFocus);
-
- try {
- WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
- mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus);
- } catch (Exception e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Unable to update focus, %s", TAG, e);
- }
- }
-
private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback {
private final View mView;
private final int mZOrder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 613791c..7c15637 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -328,7 +328,7 @@
return menuUiBounds;
}
- void transitionToMenuMode(int menuMode, boolean resetMenu) {
+ void transitionToMenuMode(int menuMode) {
switch (menuMode) {
case MODE_NO_MENU:
hideAllUserControls();
@@ -337,7 +337,7 @@
showMoveMenu();
break;
case MODE_ALL_ACTIONS_MENU:
- showAllActionsMenu(resetMenu);
+ showAllActionsMenu();
break;
default:
throw new IllegalArgumentException(
@@ -362,13 +362,13 @@
mEduTextDrawer.closeIfNeeded();
}
- private void showAllActionsMenu(boolean resetMenu) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showAllActionsMenu(), resetMenu %b", TAG, resetMenu);
+ void resetMenu() {
+ scrollToFirstAction();
+ }
- if (resetMenu) {
- scrollToFirstAction();
- }
+ private void showAllActionsMenu() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showAllActionsMenu()", TAG);
if (mCurrentMenuMode == MODE_ALL_ACTIONS_MENU) return;
@@ -431,12 +431,6 @@
}
}
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- mListener.onPipWindowFocusChanged(hasWindowFocus);
- }
-
private void animateAlphaTo(float alpha, View view) {
if (view.getAlpha() == alpha) {
return;
@@ -483,28 +477,28 @@
if (event.getAction() == ACTION_UP) {
if (event.getKeyCode() == KEYCODE_BACK) {
- mListener.onBackPress();
+ mListener.onExitCurrentMenuMode();
return true;
}
- if (mA11yManager.isEnabled()) {
- return super.dispatchKeyEvent(event);
- }
-
- switch (event.getKeyCode()) {
- case KEYCODE_DPAD_UP:
- case KEYCODE_DPAD_DOWN:
- case KEYCODE_DPAD_LEFT:
- case KEYCODE_DPAD_RIGHT:
- return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
- event);
- case KEYCODE_ENTER:
- case KEYCODE_DPAD_CENTER:
- return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
- default:
- break;
+ if (mCurrentMenuMode == MODE_MOVE_MENU && !mA11yManager.isEnabled()) {
+ switch (event.getKeyCode()) {
+ case KEYCODE_DPAD_UP:
+ case KEYCODE_DPAD_DOWN:
+ case KEYCODE_DPAD_LEFT:
+ case KEYCODE_DPAD_RIGHT:
+ mListener.onPipMovement(event.getKeyCode());
+ return true;
+ case KEYCODE_ENTER:
+ case KEYCODE_DPAD_CENTER:
+ mListener.onExitCurrentMenuMode();
+ return true;
+ default:
+ // Dispatch key event as normal below
+ }
}
}
+
return super.dispatchKeyEvent(event);
}
@@ -529,7 +523,7 @@
if (a11yEnabled) {
mA11yDoneButton.setVisibility(VISIBLE);
mA11yDoneButton.setOnClickListener(v -> {
- mListener.onExitMoveMode();
+ mListener.onExitCurrentMenuMode();
});
mA11yDoneButton.requestFocus();
mA11yDoneButton.requestAccessibilityFocus();
@@ -626,26 +620,15 @@
interface Listener {
- void onBackPress();
+ /**
+ * Called when a button for exiting the current menu mode was pressed.
+ */
+ void onExitCurrentMenuMode();
/**
- * Called when a button for exiting move mode was pressed.
- *
- * @return true if the event was handled or false if the key event should be handled by the
- * next receiver.
+ * Called when a button to move the PiP in a certain direction, indicated by keycode.
*/
- boolean onExitMoveMode();
-
- /**
- * @return whether pip movement was handled.
- */
- boolean onPipMovement(int keycode);
-
- /**
- * Called when the TvPipMenuView loses focus. This also means that the TV PiP menu window
- * has lost focus.
- */
- void onPipWindowFocusChanged(boolean focused);
+ void onPipMovement(int keycode);
/**
* The edu text closing impacts the size of the Picture-in-Picture window and influences
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 3af1b75..05e4af3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -55,6 +55,8 @@
Consts.TAG_WM_SHELL),
WM_SHELL_FOLDABLE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
+ WM_SHELL_BUBBLES(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ "Bubbles"),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 84dcd4d..0c6adc9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -376,8 +376,8 @@
private static int estimateWindowBGColor(Drawable themeBGDrawable) {
final DrawableColorTester themeBGTester = new DrawableColorTester(
themeBGDrawable, DrawableColorTester.TRANSLUCENT_FILTER /* filterType */);
- if (themeBGTester.passFilterRatio() != 1) {
- // the window background is translucent, unable to draw
+ if (themeBGTester.passFilterRatio() < 0.5f) {
+ // more than half pixels of the window background is translucent, unable to draw
Slog.w(TAG, "Window background is translucent, fill background with black color");
return getSystemBGColor();
} else {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index 9cc9fb9..55039f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.os.SystemClock
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 26aca18..b007e6b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.view.WindowInsets
import android.view.WindowManager
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index bf686d6..e38c4c3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,11 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index c003da6..b4cedd9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.app.Activity
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -28,6 +27,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index cb5a60d..42be818 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.graphics.Rect
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -25,6 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index c315e74..a236126 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.app.Activity
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -27,6 +26,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 3702be9..6b97169 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.EdgeExtensionComponentMatcher
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 8b90630..51588569 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByDividerBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 50f6a38..fc6c2b3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index cc3b783..8b1689a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DragDividerToResizeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index f8d1e1f..99613f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromAllAppsBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index ff5d935..756a7fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromNotificationBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index 7c71077..121b46a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromShortcutBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 8371706..99deb92 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromTaskbarBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 0bfdbb4..212a4e3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenFromOverviewBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 88bbc0e..284c32e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromAnotherAppBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index e85dc24..9e6448f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromHomeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index f7a9ed0..8e28712 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromRecentBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 66f9b85..fb0193b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBetweenSplitPairsBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 4c44028..f3145c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -27,6 +26,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 2cc28ac..0ae2908 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,9 +55,9 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
index 3a08d32..e26dc7c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
@@ -25,18 +25,24 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnWindowFocusChangeListener;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.SystemWindows;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -50,28 +56,38 @@
@Mock
private SystemWindows mMockSystemWindows;
@Mock
- private SurfaceControl mMockPipLeash;
- @Mock
- private Handler mMockHandler;
- @Mock
- private TvPipActionsProvider mMockActionsProvider;
- @Mock
private TvPipMenuView mMockTvPipMenuView;
@Mock
private TvPipBackgroundView mMockTvPipBackgroundView;
+ private Handler mMainHandler;
private TvPipMenuController mTvPipMenuController;
+ private OnWindowFocusChangeListener mFocusChangeListener;
@Before
public void setUp() {
assumeTrue(isTelevision());
MockitoAnnotations.initMocks(this);
+ mMainHandler = new Handler(Looper.getMainLooper());
+
+ final ViewTreeObserver mockMenuTreeObserver = mock(ViewTreeObserver.class);
+ doReturn(mockMenuTreeObserver).when(mMockTvPipMenuView).getViewTreeObserver();
mTvPipMenuController = new TestTvPipMenuController();
mTvPipMenuController.setDelegate(mMockDelegate);
- mTvPipMenuController.setTvPipActionsProvider(mMockActionsProvider);
- mTvPipMenuController.attach(mMockPipLeash);
+ mTvPipMenuController.setTvPipActionsProvider(mock(TvPipActionsProvider.class));
+ mTvPipMenuController.attach(mock(SurfaceControl.class));
+ mFocusChangeListener = captureFocusChangeListener(mockMenuTreeObserver);
+ }
+
+ private OnWindowFocusChangeListener captureFocusChangeListener(
+ ViewTreeObserver mockTreeObserver) {
+ final ArgumentCaptor<OnWindowFocusChangeListener> focusChangeListenerCaptor =
+ ArgumentCaptor.forClass(OnWindowFocusChangeListener.class);
+ verify(mockTreeObserver).addOnWindowFocusChangeListener(
+ focusChangeListenerCaptor.capture());
+ return focusChangeListenerCaptor.getValue();
}
@Test
@@ -81,24 +97,25 @@
@Test
public void testSwitch_FromNoMenuMode_ToMoveMode() {
- showAndAssertMoveMenu();
+ showAndAssertMoveMenu(true);
}
@Test
public void testSwitch_FromNoMenuMode_ToAllActionsMode() {
- showAndAssertAllActionsMenu();
+ showAndAssertAllActionsMenu(true);
}
@Test
public void testSwitch_FromMoveMode_ToAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ showAndAssertMoveMenu(true);
+ showAndAssertAllActionsMenu(false);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testSwitch_FromAllActionsMode_ToMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
}
@Test
@@ -110,187 +127,282 @@
@Test
public void testCloseMenu_MoveMode() {
- showAndAssertMoveMenu();
+ showAndAssertMoveMenu(true);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testCloseMenu_AllActionsMode() {
- showAndAssertAllActionsMenu();
+ showAndAssertAllActionsMenu(true);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
+ }
+
+ @Test
+ public void testCloseMenu_MoveModeFollowedByMoveMode() {
+ showAndAssertMoveMenu(true);
+ showAndAssertMoveMenu(false);
+
+ closeMenuAndAssertMenuClosed(true);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testCloseMenu_MoveModeFollowedByAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ showAndAssertMoveMenu(true);
+ showAndAssertAllActionsMenu(false);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
}
@Test
public void testCloseMenu_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_NoMenuMode() {
- mTvPipMenuController.onExitMoveMode();
- assertMenuIsOpen(false);
- verify(mMockDelegate, never()).onMenuClosed();
+ public void testCloseMenu_AllActionsModeFollowedByAllActionsMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertAllActionsMenu(false);
+
+ closeMenuAndAssertMenuClosed(true);
+ verify(mMockDelegate, never()).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_MoveMode() {
- showAndAssertMoveMenu();
+ public void testExitMenuMode_NoMenuMode() {
+ mTvPipMenuController.onExitCurrentMenuMode();
+ assertMenuIsOpen(false);
+ verify(mMockDelegate, never()).onMenuClosed();
+ verify(mMockDelegate, never()).onInMoveModeChanged();
+ }
- mTvPipMenuController.onExitMoveMode();
+ @Test
+ public void testExitMenuMode_MoveMode() {
+ showAndAssertMoveMenu(true);
+
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
assertMenuClosed();
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_AllActionsMode() {
- showAndAssertAllActionsMenu();
+ public void testExitMenuMode_AllActionsMode() {
+ showAndAssertAllActionsMenu(true);
- mTvPipMenuController.onExitMoveMode();
- assertMenuIsInAllActionsMode();
-
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
}
@Test
- public void testExitMoveMode_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ public void testExitMenuMode_AllActionsModeFollowedByMoveMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
- mTvPipMenuController.onExitMoveMode();
- assertMenuIsInAllActionsMode();
- verify(mMockDelegate, times(2)).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
- verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
- }
-
- @Test
- public void testOnBackPress_NoMenuMode() {
- mTvPipMenuController.onBackPress();
- assertMenuIsOpen(false);
- verify(mMockDelegate, never()).onMenuClosed();
- }
-
- @Test
- public void testOnBackPress_MoveMode() {
- showAndAssertMoveMenu();
-
- pressBackAndAssertMenuClosed();
- verify(mMockDelegate, times(2)).onInMoveModeChanged();
- }
-
- @Test
- public void testOnBackPress_AllActionsMode() {
- showAndAssertAllActionsMenu();
-
- pressBackAndAssertMenuClosed();
- }
-
- @Test
- public void testOnBackPress_MoveModeFollowedByAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ assertSwitchedToAllActionsMode(2);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- pressBackAndAssertMenuClosed();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
}
@Test
- public void testOnBackPress_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ public void testExitMenuMode_AllActionsModeFollowedByAllActionsMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertAllActionsMenu(false);
- mTvPipMenuController.onBackPress();
- assertMenuIsInAllActionsMode();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+ verify(mMockDelegate, never()).onInMoveModeChanged();
+ }
+
+ @Test
+ public void testExitMenuMode_MoveModeFollowedByAllActionsMode() {
+ showAndAssertMoveMenu(true);
+
+ showAndAssertAllActionsMenu(false);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
- verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
- pressBackAndAssertMenuClosed();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+ }
+
+ @Test
+ public void testExitMenuMode_MoveModeFollowedByMoveMode() {
+ showAndAssertMoveMenu(true);
+ showAndAssertMoveMenu(false);
+
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testOnPipMovement_NoMenuMode() {
- assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+ moveAndAssertMoveSuccessful(false);
}
@Test
public void testOnPipMovement_MoveMode() {
- showAndAssertMoveMenu();
- assertPipMoveSuccessful(true, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
- verify(mMockDelegate).movePip(eq(TEST_MOVE_KEYCODE));
+ showAndAssertMoveMenu(true);
+ moveAndAssertMoveSuccessful(true);
}
@Test
public void testOnPipMovement_AllActionsMode() {
- showAndAssertAllActionsMenu();
- assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+ showAndAssertAllActionsMenu(true);
+ moveAndAssertMoveSuccessful(false);
}
@Test
- public void testOnPipWindowFocusChanged_NoMenuMode() {
- mTvPipMenuController.onPipWindowFocusChanged(false);
- assertMenuIsOpen(false);
- }
+ public void testUnexpectedFocusChanges() {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
- @Test
- public void testOnPipWindowFocusChanged_MoveMode() {
- showAndAssertMoveMenu();
- mTvPipMenuController.onPipWindowFocusChanged(false);
+ mFocusChangeListener.onWindowFocusChanged(false);
assertMenuClosed();
+
+ showAndAssertMoveMenu(true);
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed(2);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testOnPipWindowFocusChanged_AllActionsMode() {
- showAndAssertAllActionsMenu();
- mTvPipMenuController.onPipWindowFocusChanged(false);
- assertMenuClosed();
- }
-
- private void showAndAssertMoveMenu() {
- mTvPipMenuController.showMovementMenu();
- assertMenuIsInMoveMode();
- verify(mMockDelegate).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_MOVE_MENU), eq(false));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_MOVE_MENU));
- }
-
- private void showAndAssertAllActionsMenu() {
+ public void testAsyncScenario_AllActionsModeRequestFollowedByAsyncMoveModeRequest() {
mTvPipMenuController.showMenu();
- assertMenuIsInAllActionsMode();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(true));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+ // Artificially delaying the focus change update and adding a move request to simulate an
+ // async problematic situation.
+ mTvPipMenuController.showMovementMenu();
+ // The first focus change update arrives
+ mFocusChangeListener.onWindowFocusChanged(true);
+
+ // We expect that the TvPipMenuController will directly switch to the "pending" menu mode
+ // - MODE_MOVE_MENU, because no change of focus is needed.
+ assertSwitchedToMoveMode();
}
- private void closeMenuAndAssertMenuClosed() {
+ @Test
+ public void testAsyncScenario_MoveModeRequestFollowedByAsyncAllActionsModeRequest() {
+ mTvPipMenuController.showMovementMenu();
+ mTvPipMenuController.showMenu();
+
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
+ verify(mMockDelegate, never()).onInMoveModeChanged();
+ }
+
+ @Test
+ public void testAsyncScenario_DropObsoleteIntermediateModeSwitchRequests() {
+ mTvPipMenuController.showMovementMenu();
mTvPipMenuController.closeMenu();
+
+ // Focus change from showMovementMenu() call.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToMoveMode();
+ verify(mMockDelegate).onInMoveModeChanged();
+
+ // Focus change from closeMenu() call.
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
+
+ // Unexpected focus gain should open MODE_ALL_ACTIONS_MENU.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
+
+ mTvPipMenuController.closeMenu();
+ mTvPipMenuController.showMovementMenu();
+
+ assertSwitchedToMoveMode(2);
+
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed(2);
+
+ // Closing the menu resets the default menu mode, so the next focus gain opens the menu in
+ // the default mode - MODE_ALL_ACTIONS_MENU.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(2);
+ verify(mMockDelegate, times(4)).onInMoveModeChanged();
+
+ }
+
+ private void showAndAssertMoveMenu(boolean focusChange) {
+ mTvPipMenuController.showMovementMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ }
+ assertSwitchedToMoveMode();
+ }
+
+ private void assertSwitchedToMoveMode() {
+ assertSwitchedToMoveMode(1);
+ }
+
+ private void assertSwitchedToMoveMode(int times) {
+ assertMenuIsInMoveMode();
+ verify(mMockDelegate, times(2 * times - 1)).onInMoveModeChanged();
+ verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+ verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+ }
+
+ private void showAndAssertAllActionsMenu(boolean focusChange) {
+ showAndAssertAllActionsMenu(focusChange, 1);
+ }
+
+ private void showAndAssertAllActionsMenu(boolean focusChange, int times) {
+ mTvPipMenuController.showMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ }
+
+ assertSwitchedToAllActionsMode(times);
+ }
+
+ private void assertSwitchedToAllActionsMode(int times) {
+ assertMenuIsInAllActionsMode();
+ verify(mMockTvPipMenuView, times(times))
+ .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+ verify(mMockTvPipBackgroundView, times(times))
+ .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+ }
+
+ private void closeMenuAndAssertMenuClosed(boolean focusChange) {
+ mTvPipMenuController.closeMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(false);
+ }
assertMenuClosed();
}
- private void pressBackAndAssertMenuClosed() {
- mTvPipMenuController.onBackPress();
- assertMenuClosed();
+ private void moveAndAssertMoveSuccessful(boolean expectedSuccess) {
+ mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE);
+ verify(mMockDelegate, times(expectedSuccess ? 1 : 0)).movePip(eq(TEST_MOVE_KEYCODE));
}
private void assertMenuClosed() {
+ assertMenuClosed(1);
+ }
+
+ private void assertMenuClosed(int times) {
assertMenuIsOpen(false);
- verify(mMockDelegate).onMenuClosed();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_NO_MENU), eq(false));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_NO_MENU));
+ verify(mMockDelegate, times(times)).onMenuClosed();
+ verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
+ verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
}
private void assertMenuIsOpen(boolean open) {
@@ -312,15 +424,10 @@
assertMenuIsOpen(true);
}
- private void assertPipMoveSuccessful(boolean expected, boolean actual) {
- assertTrue("Should " + (expected ? "" : "not ") + "move PiP when the menu is in mode "
- + mTvPipMenuController.getMenuModeString(), expected == actual);
- }
-
private class TestTvPipMenuController extends TvPipMenuController {
TestTvPipMenuController() {
- super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMockHandler);
+ super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMainHandler);
}
@Override
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8191f5e..a958a09 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -15,6 +15,8 @@
*/
#include "FrameInfo.h"
+#include <gui/TraceUtils.h>
+
#include <cstring>
namespace android {
@@ -51,6 +53,30 @@
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+ mSkippedFrameReason.reset();
+}
+
+const char* toString(SkippedFrameReason reason) {
+ switch (reason) {
+ case SkippedFrameReason::DrawingOff:
+ return "DrawingOff";
+ case SkippedFrameReason::ContextIsStopped:
+ return "ContextIsStopped";
+ case SkippedFrameReason::NothingToDraw:
+ return "NothingToDraw";
+ case SkippedFrameReason::NoOutputTarget:
+ return "NoOutputTarget";
+ case SkippedFrameReason::NoBuffer:
+ return "NoBuffer";
+ case SkippedFrameReason::AlreadyDrawn:
+ return "AlreadyDrawn";
+ }
+}
+
+void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) {
+ ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason));
+ addFlag(FrameInfoFlags::SkippedFrame);
+ mSkippedFrameReason = reason;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index b15b6cb..f7ad139 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -16,15 +16,17 @@
#ifndef FRAMEINFO_H_
#define FRAMEINFO_H_
-#include "utils/Macros.h"
-
#include <cutils/compiler.h>
+#include <memory.h>
#include <utils/Timers.h>
#include <array>
-#include <memory.h>
+#include <optional>
#include <string>
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -186,8 +188,14 @@
return mFrameInfo[static_cast<int>(index)];
}
+ void setSkippedFrameReason(SkippedFrameReason reason);
+ inline std::optional<SkippedFrameReason> getSkippedFrameReason() const {
+ return mSkippedFrameReason;
+ }
+
private:
int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
+ std::optional<SkippedFrameReason> mSkippedFrameReason;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 687e4dd..59f2169 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -148,7 +148,7 @@
int fast_i = 0, janky_i = 0;
// Set the bottom of all the shapes to the baseline
for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
- if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+ if (mFrameSource[fi].getSkippedFrameReason()) {
continue;
}
float lineWidth = baseLineWidth;
@@ -181,7 +181,7 @@
int janky_i = (mNumJankyRects - 1) * 4;
for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
- if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+ if (mFrameSource[fi].getSkippedFrameReason()) {
continue;
}
diff --git a/libs/hwui/SkippedFrameInfo.h b/libs/hwui/SkippedFrameInfo.h
new file mode 100644
index 0000000..de56d9a
--- /dev/null
+++ b/libs/hwui/SkippedFrameInfo.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::uirenderer {
+
+enum class SkippedFrameReason {
+ DrawingOff,
+ ContextIsStopped,
+ NothingToDraw,
+ NoOutputTarget,
+ NoBuffer,
+ AlreadyDrawn,
+};
+
+} /* namespace android::uirenderer */
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 2bff9cb..ea25f68 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,14 +16,16 @@
#pragma once
-#include "Properties.h"
-#include "utils/Macros.h"
-
#include <utils/Timers.h>
-#include "SkSize.h"
+#include <optional>
#include <string>
+#include "Properties.h"
+#include "SkSize.h"
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -110,13 +112,13 @@
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
bool requiresUiRedraw = false;
- // This is set to true if draw() can be called this frame
- // false means that we must delay until the next vsync pulse as frame
+ // This is set to nullopt if draw() can be called this frame
+ // A value means that we must delay until the next vsync pulse as frame
// production is outrunning consumption
- // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+ // NOTE that if this has a value CanvasContext will set either requiresUiRedraw
// *OR* will post itself for the next vsync automatically, use this
// only to avoid calling draw()
- bool canDrawThisFrame = true;
+ std::optional<SkippedFrameReason> skippedFrameReason;
// Sentinel for animatedImageDelay meaning there is no need to post such
// a message.
static constexpr nsecs_t kNoAnimatedImageDelay = -1;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 23b3074..530c654 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,6 +17,7 @@
#include "SkiaOpenGLPipeline.h"
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/gl/GrGLTypes.h>
#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2ef7802..2e0de3f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -357,8 +357,9 @@
return true;
}
-static bool wasSkipped(FrameInfo* info) {
- return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
+static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) {
+ if (info) return info->getSkippedFrameReason();
+ return std::nullopt;
}
bool CanvasContext::isSwapChainStuffed() {
@@ -407,13 +408,26 @@
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
- if (wasSkipped(mCurrentFrameInfo)) {
+ if (const auto reason = wasSkipped(mCurrentFrameInfo)) {
// Use the oldest skipped frame in case we skip more than a single frame
if (!mSkippedFrameInfo) {
- mSkippedFrameInfo.emplace();
- mSkippedFrameInfo->vsyncId =
- mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
- mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ switch (*reason) {
+ case SkippedFrameReason::AlreadyDrawn:
+ case SkippedFrameReason::NoBuffer:
+ case SkippedFrameReason::NoOutputTarget:
+ mSkippedFrameInfo.emplace();
+ mSkippedFrameInfo->vsyncId =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ mSkippedFrameInfo->startTime =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ break;
+ case SkippedFrameReason::DrawingOff:
+ case SkippedFrameReason::ContextIsStopped:
+ case SkippedFrameReason::NothingToDraw:
+ // Do not report those as skipped frames as there was no frame expected to be
+ // drawn
+ break;
+ }
}
} else {
mCurrentFrameInfo = mJankTracker.startFrame();
@@ -427,7 +441,7 @@
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
info.damageGenerationId = mDamageId++;
- info.out.canDrawThisFrame = true;
+ info.out.skippedFrameReason = std::nullopt;
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -447,8 +461,8 @@
mIsDirty = true;
if (CC_UNLIKELY(!hasOutputTarget())) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
return;
}
@@ -463,23 +477,23 @@
if (vsyncDelta < 2_ms) {
// Already drew for this vsync pulse, UI draw request missed
// the deadline for RT animations
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn;
}
} else {
- info.out.canDrawThisFrame = true;
+ info.out.skippedFrameReason = std::nullopt;
}
// TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
// be an allowable combination?
if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw;
}
- if (info.out.canDrawThisFrame) {
+ if (!info.out.skippedFrameReason) {
int err = mNativeSurface->reserveNext();
if (err != OK) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
if (err != TIMED_OUT) {
// A timed out surface can still recover, but assume others are permanently dead.
@@ -488,11 +502,11 @@
}
}
} else {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
}
bool postedFrameCallback = false;
- if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+ if (info.out.hasAnimations || info.out.skippedFrameReason) {
if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
info.out.requiresUiRedraw = true;
}
@@ -558,9 +572,20 @@
mSyncDelayDuration = 0;
mIdleDuration = 0;
- if (!Properties::isDrawingEnabled() ||
- (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> {
+ if (!Properties::isDrawingEnabled()) {
+ return SkippedFrameReason::DrawingOff;
+ }
+
+ if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+ return SkippedFrameReason::NothingToDraw;
+ }
+
+ return std::nullopt;
+ }();
+ if (skippedFrameReason) {
+ mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
// free its staging buffers.
@@ -904,7 +929,7 @@
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
- if (info.out.canDrawThisFrame) {
+ if (!info.out.skippedFrameReason) {
draw(info.out.solelyTextureViewUpdates);
} else {
// wait on fences so tasks don't overlap next frame
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 53b43ba..1b333bf 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -104,7 +104,7 @@
info.forceDrawFrame = mForceDrawFrame;
mForceDrawFrame = false;
canUnblockUiThread = syncFrameState(info);
- canDrawThisFrame = info.out.canDrawThisFrame;
+ canDrawThisFrame = !info.out.skippedFrameReason.has_value();
solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
if (mFrameCommitCallback) {
@@ -192,11 +192,12 @@
if (CC_UNLIKELY(!hasTarget || !canDraw)) {
if (!hasTarget) {
mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
+ info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
} else {
// If we have a surface but can't draw we must be stopped
mSyncResult |= SyncResult::ContextIsStopped;
+ info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped;
}
- info.out.canDrawThisFrame = false;
}
if (info.out.hasAnimations) {
@@ -204,7 +205,7 @@
mSyncResult |= SyncResult::UIRedrawRequired;
}
}
- if (!info.out.canDrawThisFrame) {
+ if (info.out.skippedFrameReason) {
mSyncResult |= SyncResult::FrameDropped;
}
// If prepareTextures is false, we ran out of texture cache space
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 94a061a..d6921c8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -985,6 +985,11 @@
void onRequestCreateControllerByManagerOnHandler(
RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
+ Log.i(
+ TAG,
+ TextUtils.formatSimple(
+ "requestCreateSessionByManager | requestId: %d, oldSession: %s, route: %s",
+ managerRequestId, oldSession, route));
RoutingController controller;
if (oldSession.isSystemSession()) {
controller = getSystemController();
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 254eb44..7f3792d 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -46,7 +46,10 @@
cc_library_shared {
name: "libandroid",
- defaults: ["libandroid_defaults"],
+ defaults: [
+ "libandroid_defaults",
+ "android.hardware.power-ndk_shared",
+ ],
srcs: [
"activity_manager.cpp",
@@ -95,7 +98,6 @@
"libpowermanager",
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
- "android.hardware.power-V4-ndk",
"libnativedisplay",
],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index d74f9b7..b0af09c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -334,6 +334,7 @@
APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu
APerformanceHint_closeSession; # introduced=Tiramisu
APerformanceHint_setThreads; # introduced=UpsideDownCake
+ APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 6198f40..c25df6e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "perf_hint"
#include <aidl/android/hardware/power/SessionHint.h>
+#include <aidl/android/hardware/power/SessionMode.h>
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
#include <android/performance_hint.h>
@@ -36,6 +37,7 @@
using namespace std::chrono_literals;
using AidlSessionHint = aidl::android::hardware::power::SessionHint;
+using AidlSessionMode = aidl::android::hardware::power::SessionMode;
struct APerformanceHintSession;
@@ -72,6 +74,7 @@
int sendHint(SessionHint hint);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
+ int setPreferPowerEfficiency(bool enabled);
private:
friend struct APerformanceHintManager;
@@ -307,6 +310,18 @@
return 0;
}
+int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) {
+ binder::Status ret =
+ mHintSession->setMode(static_cast<int32_t>(AidlSessionMode::POWER_EFFICIENCY), enabled);
+
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession setPreferPowerEfficiency failed: %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
+ return EPIPE;
+ }
+ return OK;
+}
+
// ===================================== C API
APerformanceHintManager* APerformanceHint_getManager() {
return APerformanceHintManager::getInstance();
@@ -357,6 +372,10 @@
->getThreadIds(threadIds, size);
}
+int APerformanceHint_setPreferPowerEfficiency(APerformanceHintSession* session, bool enabled) {
+ return session->setPreferPowerEfficiency(enabled);
+}
+
void APerformanceHint_setIHintManagerForTesting(void* iManager) {
delete gHintManagerForTesting;
gHintManagerForTesting = nullptr;
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 6f7562b..22d33b1 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -56,7 +56,8 @@
(const ::std::vector<int64_t>& actualDurationNanos,
const ::std::vector<int64_t>& timeStampNanos),
(override));
- MOCK_METHOD(Status, sendHint, (int32_t hints), (override));
+ MOCK_METHOD(Status, sendHint, (int32_t hint), (override));
+ MOCK_METHOD(Status, setMode, (int32_t mode, bool enabled), (override));
MOCK_METHOD(Status, close, (), (override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
};
@@ -190,3 +191,51 @@
result = APerformanceHint_setThreads(session, invalidTids.data(), invalidTids.size());
EXPECT_EQ(EPERM, result);
}
+
+TEST_F(PerformanceHintTest, SetPowerEfficient) {
+ APerformanceHintManager* manager = createManager();
+
+ std::vector<int32_t> tids;
+ tids.push_back(1);
+ tids.push_back(2);
+ int64_t targetDuration = 56789L;
+
+ StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>();
+ sp<IHintSession> session_sp(iSession);
+
+ EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status())));
+
+ APerformanceHintSession* session =
+ APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
+ ASSERT_TRUE(session);
+
+ EXPECT_CALL(*iSession, setMode(_, Eq(true))).Times(Exactly(1));
+ int result = APerformanceHint_setPreferPowerEfficiency(session, true);
+ EXPECT_EQ(0, result);
+
+ EXPECT_CALL(*iSession, setMode(_, Eq(false))).Times(Exactly(1));
+ result = APerformanceHint_setPreferPowerEfficiency(session, false);
+ EXPECT_EQ(0, result);
+}
+
+TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) {
+ APerformanceHintManager* manager = createManager();
+
+ std::vector<int32_t> tids;
+ tids.push_back(1);
+ tids.push_back(2);
+ int64_t targetDuration = 0;
+
+ StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>();
+ sp<IHintSession> session_sp(iSession);
+
+ EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status())));
+
+ APerformanceHintSession* session =
+ APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
+ ASSERT_TRUE(session);
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Color/.gitignore b/packages/SettingsLib/Color/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/packages/SettingsLib/Color/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/packages/SettingsLib/Color/Android.bp b/packages/SettingsLib/Color/Android.bp
new file mode 100644
index 0000000..713b7d9
--- /dev/null
+++ b/packages/SettingsLib/Color/Android.bp
@@ -0,0 +1,15 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibColor",
+ use_resource_processor: true,
+ sdk_version: "current",
+ min_sdk_version: "28",
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
diff --git a/packages/SettingsLib/Color/AndroidManifest.xml b/packages/SettingsLib/Color/AndroidManifest.xml
new file mode 100644
index 0000000..31e9d23
--- /dev/null
+++ b/packages/SettingsLib/Color/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+
+<manifest package="com.android.settingslib.color" />
diff --git a/packages/SettingsLib/Color/build.gradle.kts b/packages/SettingsLib/Color/build.gradle.kts
new file mode 100644
index 0000000..881bf14
--- /dev/null
+++ b/packages/SettingsLib/Color/build.gradle.kts
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+}
+
+android {
+ namespace = "com.android.settingslib.color"
+
+ sourceSets {
+ sourceSets.getByName("main") {
+ res.setSrcDirs(listOf("res"))
+ manifest.srcFile("AndroidManifest.xml")
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml
similarity index 97%
rename from packages/SettingsLib/Spa/spa/res/values/colors.xml
rename to packages/SettingsLib/Color/res/values/colors.xml
index ca4a0b2..b0b9b10 100644
--- a/packages/SettingsLib/Spa/spa/res/values/colors.xml
+++ b/packages/SettingsLib/Color/res/values/colors.xml
@@ -16,8 +16,6 @@
-->
<resources>
- <color name="settingslib_protection_color">@android:color/white</color>
-
<!-- Dynamic colors-->
<color name="settingslib_color_blue600">#1a73e8</color>
<color name="settingslib_color_blue400">#669df6</color>
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index e80eb66..24ccab2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -14,6 +14,7 @@
resource_dirs: ["res"],
static_libs: [
+ "SettingsLibColor",
"androidx.preference_preference",
"lottie",
],
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index accaa67..e53a43e 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -17,51 +17,4 @@
<resources>
<color name="settingslib_protection_color">@android:color/white</color>
-
- <!-- Dynamic colors-->
- <color name="settingslib_color_blue600">#1a73e8</color>
- <color name="settingslib_color_blue400">#669df6</color>
- <color name="settingslib_color_blue300">#8ab4f8</color>
- <color name="settingslib_color_blue100">#d2e3fc</color>
- <color name="settingslib_color_blue50">#e8f0fe</color>
- <color name="settingslib_color_green600">#1e8e3e</color>
- <color name="settingslib_color_green500">#34A853</color>
- <color name="settingslib_color_green400">#5bb974</color>
- <color name="settingslib_color_green100">#ceead6</color>
- <color name="settingslib_color_green50">#e6f4ea</color>
- <color name="settingslib_color_red600">#d93025</color>
- <color name="settingslib_color_red500">#B3261E</color>
- <color name="settingslib_color_red400">#ee675c</color>
- <color name="settingslib_color_red100">#fad2cf</color>
- <color name="settingslib_color_red50">#fce8e6</color>
- <color name="settingslib_color_yellow600">#f9ab00</color>
- <color name="settingslib_color_yellow400">#fcc934</color>
- <color name="settingslib_color_yellow100">#feefc3</color>
- <color name="settingslib_color_yellow50">#fef7e0</color>
- <color name="settingslib_color_grey900">#202124</color>
- <color name="settingslib_color_grey800">#3c4043</color>
- <color name="settingslib_color_grey700">#5f6368</color>
- <color name="settingslib_color_grey600">#80868b</color>
- <color name="settingslib_color_grey500">#9AA0A6</color>
- <color name="settingslib_color_grey400">#bdc1c6</color>
- <color name="settingslib_color_grey300">#dadce0</color>
- <color name="settingslib_color_grey200">#e8eaed</color>
- <color name="settingslib_color_grey100">#f1f3f4</color>
- <color name="settingslib_color_grey50">#f8f9fa</color>
- <color name="settingslib_color_orange600">#e8710a</color>
- <color name="settingslib_color_orange400">#fa903e</color>
- <color name="settingslib_color_orange300">#fcad70</color>
- <color name="settingslib_color_orange100">#fedfc8</color>
- <color name="settingslib_color_pink600">#e52592</color>
- <color name="settingslib_color_pink400">#ff63b8</color>
- <color name="settingslib_color_pink300">#ff8bcb</color>
- <color name="settingslib_color_pink100">#fdcfe8</color>
- <color name="settingslib_color_purple600">#9334e6</color>
- <color name="settingslib_color_purple400">#af5cf7</color>
- <color name="settingslib_color_purple300">#c58af9</color>
- <color name="settingslib_color_purple100">#e9d2fd</color>
- <color name="settingslib_color_cyan600">#12b5c8</color>
- <color name="settingslib_color_cyan400">#4ecde6</color>
- <color name="settingslib_color_cyan300">#78d9ec</color>
- <color name="settingslib_color_cyan100">#cbf0f8</color>
</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
index 07102d5..82b7e04 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
@@ -23,6 +23,8 @@
import android.graphics.PorterDuffColorFilter;
import android.util.Pair;
+import com.android.settingslib.color.R;
+
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index f166a18..0447ef8 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -21,6 +21,8 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import com.android.settingslib.color.R;
+
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index e76139f..1cc2867 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidBasePlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -25,7 +26,7 @@
}
allprojects {
- extra["jetpackComposeVersion"] = "1.6.0-alpha01"
+ extra["jetpackComposeVersion"] = "1.6.0-alpha02"
}
subprojects {
@@ -47,10 +48,10 @@
afterEvaluate {
plugins.withType<AndroidBasePlugin> {
- configure<BaseExtension> {
+ the(CommonExtension::class).apply {
if (buildFeatures.compose == true) {
composeOptions {
- kotlinCompilerExtensionVersion = "1.4.4"
+ kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
}
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index ee40b02..0f467b9 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -16,8 +16,9 @@
[versions]
agp = "8.1.0"
+compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
-kotlin = "1.8.10"
+kotlin = "1.9.0"
truth = "1.1"
[libraries]
diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts
index 9909781..aac0fe9 100644
--- a/packages/SettingsLib/Spa/settings.gradle.kts
+++ b/packages/SettingsLib/Spa/settings.gradle.kts
@@ -40,3 +40,5 @@
include(":spa")
include(":gallery")
include(":testutils")
+include(":SettingsLibColor")
+project(":SettingsLibColor").projectDir = File(rootDir, "../Color")
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
index 7f5948c..6df0e99 100644
--- a/packages/SettingsLib/Spa/spa/Android.bp
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -24,6 +24,7 @@
srcs: ["src/**/*.kt"],
use_resource_processor: true,
static_libs: [
+ "SettingsLibColor",
"androidx.slice_slice-builders",
"androidx.slice_slice-core",
"androidx.slice_slice-view",
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 377e72ed..84198de 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -52,6 +52,7 @@
}
dependencies {
+ api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0-alpha03")
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
@@ -62,7 +63,7 @@
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.0-beta01")
+ api("androidx.navigation:navigation-compose:2.7.0-rc01")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
index 5f7fe85..a6cc3a9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
@@ -33,7 +33,7 @@
import com.airbnb.lottie.compose.rememberLottieComposition
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.airbnb.lottie.compose.rememberLottieDynamicProperty
-import com.android.settingslib.spa.R
+import com.android.settingslib.color.R
@Composable
fun Lottie(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index fe3ef5d..dbc3bf7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,6 +22,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
@@ -520,9 +521,13 @@
if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE
|| type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
|| type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) {
- MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
- if (mutingExpectedDevice != null) {
- mMediaDevices.add(mutingExpectedDevice);
+ if (isTv()) {
+ mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+ } else {
+ MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
+ if (mutingExpectedDevice != null) {
+ mMediaDevices.add(mutingExpectedDevice);
+ }
}
break;
}
@@ -542,6 +547,12 @@
}
}
+ private boolean isTv() {
+ PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
private MediaDevice getMutingExpectedDevice() {
if (mBluetoothAdapter == null
|| mAudioManager.getMutingExpectedDevice() == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9234d37..147412d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -22,6 +22,7 @@
import static android.media.MediaRoute2Info.TYPE_GROUP;
import static android.media.MediaRoute2Info.TYPE_HDMI;
import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -82,7 +83,8 @@
MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE,
MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
MediaDeviceType.TYPE_CAST_DEVICE,
- MediaDeviceType.TYPE_CAST_GROUP_DEVICE})
+ MediaDeviceType.TYPE_CAST_GROUP_DEVICE,
+ MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER})
public @interface MediaDeviceType {
int TYPE_UNKNOWN = 0;
int TYPE_PHONE_DEVICE = 1;
@@ -92,6 +94,7 @@
int TYPE_BLUETOOTH_DEVICE = 5;
int TYPE_CAST_DEVICE = 6;
int TYPE_CAST_GROUP_DEVICE = 7;
+ int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 8;
}
@Retention(RetentionPolicy.SOURCE)
@@ -161,6 +164,9 @@
case TYPE_BLE_HEADSET:
mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
break;
+ case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+ mType = MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
+ break;
case TYPE_UNKNOWN:
case TYPE_REMOTE_TV:
case TYPE_REMOTE_SPEAKER:
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java
new file mode 100644
index 0000000..1134d13
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java
@@ -0,0 +1,127 @@
+/*
+ * 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.wifi.dpp;
+
+import android.content.Intent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+
+import java.util.List;
+
+
+/**
+ * Wifi dpp intent helper functions to share between the Settings App and SystemUI.
+ */
+public class WifiDppIntentHelper {
+ static final String EXTRA_WIFI_SECURITY = "security";
+
+ /** The data corresponding to {@code WifiConfiguration} SSID */
+ static final String EXTRA_WIFI_SSID = "ssid";
+
+ /** The data corresponding to {@code WifiConfiguration} preSharedKey */
+ static final String EXTRA_WIFI_PRE_SHARED_KEY = "preSharedKey";
+
+ /** The data corresponding to {@code WifiConfiguration} hiddenSSID */
+ static final String EXTRA_WIFI_HIDDEN_SSID = "hiddenSsid";
+ static final String SECURITY_NO_PASSWORD = "nopass"; //open network or OWE
+ static final String SECURITY_WEP = "WEP";
+ static final String SECURITY_WPA_PSK = "WPA";
+ static final String SECURITY_SAE = "SAE";
+
+ /**
+ * Set all extra except {@code EXTRA_WIFI_NETWORK_ID} for the intent to
+ * launch configurator activity later.
+ *
+ * @param intent the target to set extra
+ * @param wifiManager an instance of {@code WifiManager}
+ * @param wifiConfiguration the Wi-Fi network for launching configurator activity
+ */
+ public static void setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager,
+ WifiConfiguration wifiConfiguration) {
+ String ssid = removeFirstAndLastDoubleQuotes(wifiConfiguration.SSID);
+ String security = getSecurityString(wifiConfiguration);
+
+ // When the value of this key is read, the actual key is not returned, just a "*".
+ // Call privileged system API to obtain actual key.
+ String preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager,
+ wifiConfiguration));
+
+ if (!TextUtils.isEmpty(ssid)) {
+ intent.putExtra(EXTRA_WIFI_SSID, ssid);
+ }
+ if (!TextUtils.isEmpty(security)) {
+ intent.putExtra(EXTRA_WIFI_SECURITY, security);
+ }
+ if (!TextUtils.isEmpty(preSharedKey)) {
+ intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
+ }
+ intent.putExtra(EXTRA_WIFI_HIDDEN_SSID, wifiConfiguration.hiddenSSID);
+ }
+
+ private static String getSecurityString(WifiConfiguration config) {
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
+ return SECURITY_SAE;
+ }
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
+ return SECURITY_NO_PASSWORD;
+ }
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
+ || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA2_PSK)) {
+ return SECURITY_WPA_PSK;
+ }
+ return (config.wepKeys[0] == null) ? SECURITY_NO_PASSWORD : SECURITY_WEP;
+ }
+
+ private static String removeFirstAndLastDoubleQuotes(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return str;
+ }
+
+ int begin = 0;
+ int end = str.length() - 1;
+ if (str.charAt(begin) == '\"') {
+ begin++;
+ }
+ if (str.charAt(end) == '\"') {
+ end--;
+ }
+ return str.substring(begin, end + 1);
+ }
+
+ private static String getPresharedKey(WifiManager wifiManager,
+ WifiConfiguration wifiConfiguration) {
+ List<WifiConfiguration> privilegedWifiConfigurations =
+ wifiManager.getPrivilegedConfiguredNetworks();
+
+ for (WifiConfiguration privilegedWifiConfiguration : privilegedWifiConfigurations) {
+ if (privilegedWifiConfiguration.networkId == wifiConfiguration.networkId) {
+ // WEP uses a shared key hence the AuthAlgorithm.SHARED is used
+ // to identify it.
+ if (wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)
+ && wifiConfiguration.allowedAuthAlgorithms.get(
+ WifiConfiguration.AuthAlgorithm.SHARED)) {
+ return privilegedWifiConfiguration
+ .wepKeys[privilegedWifiConfiguration.wepTxKeyIndex];
+ } else {
+ return privilegedWifiConfiguration.preSharedKey;
+ }
+ }
+ }
+ return wifiConfiguration.preSharedKey;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java
new file mode 100644
index 0000000..d73df2d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.wifi.dpp;
+
+import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_HIDDEN_SSID;
+import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_PRE_SHARED_KEY;
+import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_SECURITY;
+import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_SSID;
+import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.SECURITY_NO_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiDppIntentHelperTest {
+ @Mock
+ private WifiManager mWifiManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mWifiManager.getPrivilegedConfiguredNetworks()).thenReturn(new ArrayList<>());
+ }
+
+ @Test
+ public void setConfiguratorIntentExtra_returnsCorrectValues() {
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.SSID = EXTRA_WIFI_SSID;
+ wifiConfiguration.preSharedKey = EXTRA_WIFI_PRE_SHARED_KEY;
+ wifiConfiguration.hiddenSSID = true;
+
+ Intent expected = new Intent();
+ WifiDppIntentHelper.setConfiguratorIntentExtra(expected, mWifiManager, wifiConfiguration);
+
+ assertThat(expected.getStringExtra(EXTRA_WIFI_SSID)).isEqualTo(EXTRA_WIFI_SSID);
+ assertThat(expected.getStringExtra(EXTRA_WIFI_SECURITY)).isEqualTo(SECURITY_NO_PASSWORD);
+ assertThat(expected.getStringExtra(EXTRA_WIFI_PRE_SHARED_KEY)).isEqualTo(
+ EXTRA_WIFI_PRE_SHARED_KEY);
+ assertThat(expected.getBooleanExtra(EXTRA_WIFI_HIDDEN_SSID, false))
+ .isEqualTo(true);
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bbfdc38..6c9da97 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -261,6 +261,8 @@
VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
+ VALIDATORS.put(Secure.NAVIGATION_MODE_RESTORE,
+ new DiscreteValueValidator(new String[] {"-1", "0", "1", "2"}));
VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT,
new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 99a00e4..c830d6b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -835,7 +835,11 @@
continue;
}
- if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) {
+ // Filter out Settings.Secure.NAVIGATION_MODE from modified preserve settings.
+ // Let it take part in restore process. See also b/244532342.
+ boolean isSettingPreserved = settingsToPreserve.contains(
+ getQualifiedKeyForSetting(key, contentUri));
+ if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) {
Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+ "preserved");
continue;
@@ -896,6 +900,23 @@
} else {
destination = contentUri;
}
+
+ // Value is written to NAVIGATION_MODE_RESTORE to mark navigation mode
+ // has been set before on source device.
+ // See also: b/244532342.
+ if (Settings.Secure.NAVIGATION_MODE.equals(key)) {
+ contentValues.clear();
+ contentValues.put(Settings.NameValueTable.NAME,
+ Settings.Secure.NAVIGATION_MODE_RESTORE);
+ contentValues.put(Settings.NameValueTable.VALUE, value);
+ cr.insert(destination, contentValues);
+ // Avoid restore original setting if it has been preserved.
+ if (isSettingPreserved) {
+ Log.i(TAG, "Skipping restore for setting navigation_mode "
+ + "as it is marked as preserved");
+ continue;
+ }
+ }
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5475fad..aed6e03 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -859,7 +859,8 @@
Settings.Secure.CREDENTIAL_SERVICE,
Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
- Settings.Secure.DND_CONFIGS_MIGRATED);
+ Settings.Secure.DND_CONFIGS_MIGRATED,
+ Settings.Secure.NAVIGATION_MODE_RESTORE);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 29999c2..b472982 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -423,6 +423,7 @@
"mockito-target-extended-minus-junit4",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
+ "kotlin-test",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
index c3f44f8..f7ebe2f 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
@@ -37,7 +37,14 @@
}
/** Key for a scene. */
-class SceneKey(name: String, identity: Any = Object()) : Key(name, identity) {
+class SceneKey(
+ name: String,
+ identity: Any = Object(),
+) : Key(name, identity) {
+
+ /** The unique [ElementKey] identifying this scene's root element. */
+ val rootElementKey = ElementKey(name, identity)
+
override fun toString(): String {
return "SceneKey(name=$name)"
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
index 9752f53..f4e3902 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -35,7 +35,7 @@
/** The transitions configuration of a [SceneTransitionLayout]. */
class SceneTransitions(
- val transitionSpecs: List<TransitionSpec>,
+ private val transitionSpecs: List<TransitionSpec>,
) {
private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>()
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index afd49b4..48d5638e8b 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -75,7 +75,7 @@
}
}
-private class TransitionBuilderImpl : TransitionBuilder {
+internal class TransitionBuilderImpl : TransitionBuilder {
val transformations = mutableListOf<Transformation>()
override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index b9baa793..81b9eb0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -24,7 +24,7 @@
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.background
+import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -46,6 +46,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
@@ -63,6 +64,13 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+object Bouncer {
+ object Elements {
+ val Background = ElementKey("BouncerBackground")
+ val Content = ElementKey("BouncerContent")
+ }
+}
+
/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */
@SysUISingleton
class BouncerScene
@@ -88,7 +96,7 @@
}
@Composable
-private fun BouncerScene(
+private fun SceneScope.BouncerScene(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
modifier: Modifier = Modifier,
@@ -97,84 +105,90 @@
val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState()
val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
var dialog: Dialog? by remember { mutableStateOf(null) }
+ val backgroundColor = MaterialTheme.colorScheme.surface
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(60.dp),
- modifier =
- modifier
- .fillMaxSize()
- .background(MaterialTheme.colorScheme.surface)
- .padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
- ) {
- Crossfade(
- targetState = message,
- label = "Bouncer message",
- animationSpec = if (message.isUpdateAnimated) tween() else snap(),
- ) { message ->
- Text(
- text = message.text,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyLarge,
- )
+ Box(modifier) {
+ Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
+ drawRect(color = backgroundColor)
}
- Box(Modifier.weight(1f)) {
- when (val nonNullViewModel = authMethodViewModel) {
- is PinBouncerViewModel ->
- PinBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PasswordBouncerViewModel ->
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PatternBouncerViewModel ->
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
- .align(Alignment.BottomCenter),
- )
- else -> Unit
- }
- }
-
- Button(
- onClick = viewModel::onEmergencyServicesButtonClicked,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- ),
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(60.dp),
+ modifier =
+ Modifier.element(Bouncer.Elements.Content)
+ .fillMaxSize()
+ .padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
) {
- Text(
- text = stringResource(com.android.internal.R.string.lockscreen_emergency_call),
- style = MaterialTheme.typography.bodyMedium,
- )
- }
-
- if (dialogMessage != null) {
- if (dialog == null) {
- dialog =
- dialogFactory().apply {
- setMessage(dialogMessage)
- setButton(
- DialogInterface.BUTTON_NEUTRAL,
- context.getString(R.string.ok),
- ) { _, _ ->
- viewModel.onThrottlingDialogDismissed()
- }
- setCancelable(false)
- setCanceledOnTouchOutside(false)
- show()
- }
+ Crossfade(
+ targetState = message,
+ label = "Bouncer message",
+ animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+ ) { message ->
+ Text(
+ text = message.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
}
- } else {
- dialog?.dismiss()
- dialog = null
+
+ Box(Modifier.weight(1f)) {
+ when (val nonNullViewModel = authMethodViewModel) {
+ is PinBouncerViewModel ->
+ PinBouncer(
+ viewModel = nonNullViewModel,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ is PasswordBouncerViewModel ->
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ is PatternBouncerViewModel ->
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier =
+ Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+ .align(Alignment.BottomCenter),
+ )
+ else -> Unit
+ }
+ }
+
+ Button(
+ onClick = viewModel::onEmergencyServicesButtonClicked,
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ ),
+ ) {
+ Text(
+ text = stringResource(com.android.internal.R.string.lockscreen_emergency_call),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+
+ if (dialogMessage != null) {
+ if (dialog == null) {
+ dialog =
+ dialogFactory().apply {
+ setMessage(dialogMessage)
+ setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ context.getString(R.string.ok),
+ ) { _, _ ->
+ viewModel.onThrottlingDialogDismissed()
+ }
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ show()
+ }
+ }
+ } else {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 38b751c..889c026 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -31,9 +31,17 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+
+object Notifications {
+ object Elements {
+ val Notifications = ElementKey("Notifications")
+ }
+}
@Composable
-fun Notifications(
+fun SceneScope.Notifications(
modifier: Modifier = Modifier,
) {
// TODO(b/272779828): implement.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt
index 1bb341c..c84a5e9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt
@@ -31,15 +31,27 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+
+object QuickSettings {
+ object Elements {
+ // TODO RENAME
+ val Content = ElementKey("QuickSettingsContent")
+ val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
+ val FooterActions = ElementKey("QuickSettingsFooterActions")
+ }
+}
@Composable
-fun QuickSettings(
+fun SceneScope.QuickSettings(
modifier: Modifier = Modifier,
) {
// TODO(b/272780058): implement.
Column(
modifier =
modifier
+ .element(QuickSettings.Elements.Content)
.fillMaxWidth()
.defaultMinSize(minHeight = 300.dp)
.clip(RoundedCornerShape(32.dp))
@@ -47,15 +59,19 @@
.padding(16.dp),
) {
Text(
- text = "Quick settings",
- modifier = Modifier.align(Alignment.CenterHorizontally),
+ text = "Quick settings grid",
+ modifier =
+ Modifier.element(QuickSettings.Elements.CollapsedGrid)
+ .align(Alignment.CenterHorizontally),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimary,
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = "QS footer actions",
- modifier = Modifier.align(Alignment.CenterHorizontally),
+ modifier =
+ Modifier.element(QuickSettings.Elements.FooterActions)
+ .align(Alignment.CenterHorizontally),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onPrimary,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 29763c2..e5cd439 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -16,19 +16,17 @@
package com.android.systemui.qs.ui.composable
-import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.footer.ui.compose.QuickSettings
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -69,23 +67,18 @@
}
@Composable
-private fun QuickSettingsScene(
+private fun SceneScope.QuickSettingsScene(
viewModel: QuickSettingsSceneViewModel,
modifier: Modifier = Modifier,
) {
// TODO(b/280887232): implement the real UI.
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Quick settings", style = MaterialTheme.typography.headlineMedium)
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
- }
- }
+ Box(
+ modifier
+ .fillMaxSize()
+ .clickable(onClick = { viewModel.onContentClicked() })
+ .padding(horizontal = 16.dp, vertical = 48.dp)
+ ) {
+ QuickSettings(modifier = Modifier.fillMaxHeight())
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index f91baf2..c865070 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -31,7 +31,6 @@
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
import com.android.compose.animation.scene.observableTransitionState
-import com.android.compose.animation.scene.transitions
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -78,7 +77,7 @@
SceneTransitionLayout(
currentScene = currentSceneKey.toTransitionSceneKey(),
onChangeScene = viewModel::onSceneChanged,
- transitions = transitions {},
+ transitions = SceneContainerTransitions,
state = state,
modifier = modifier.fillMaxSize(),
) {
@@ -98,7 +97,9 @@
) {
with(composableScene) {
this@scene.Content(
- modifier = Modifier.fillMaxSize(),
+ modifier =
+ Modifier.element(sceneKey.toTransitionSceneKey().rootElementKey)
+ .fillMaxSize(),
)
}
}
@@ -129,14 +130,6 @@
}
// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
-private fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
- return SceneTransitionSceneKey(
- name = toString(),
- identity = this,
- )
-}
-
-// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout.
private fun SceneTransitionSceneKey.toModel(): SceneModel {
return SceneModel(key = identity as SceneKey)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
new file mode 100644
index 0000000..404bf81
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -0,0 +1,34 @@
+package com.android.systemui.scene.ui.composable
+
+import com.android.compose.animation.scene.transitions
+import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
+import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
+
+/**
+ * Comprehensive definition of all transitions between scenes in [SceneContainer].
+ *
+ * Transitions are automatically reversible, so define only one transition per scene pair. By
+ * convention, use the more common transition direction when defining the pair order, e.g.
+ * Lockscreen to Bouncer rather than Bouncer to Lockscreen.
+ *
+ * The actual transition DSL must be placed in a separate file under the package
+ * [com.android.systemui.scene.ui.composable.transitions].
+ *
+ * Please keep the list sorted alphabetically.
+ */
+val SceneContainerTransitions = transitions {
+ from(Bouncer, to = Gone) { bouncerToGoneTransition() }
+ from(Gone, to = Shade) { goneToShadeTransition() }
+ from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() }
+ from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() }
+ from(Lockscreen, to = Shade) { lockscreenToShadeTransition() }
+ from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() }
+ from(Lockscreen, to = Gone) { lockscreenToGoneTransition() }
+ from(Shade, to = QuickSettings) { shadeToQuickSettingsTransition() }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
new file mode 100644
index 0000000..8d0d705
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
@@ -0,0 +1,15 @@
+package com.android.systemui.scene.ui.composable
+
+import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
+import com.android.systemui.scene.shared.model.SceneKey
+
+val Lockscreen = SceneKey.Lockscreen.toTransitionSceneKey()
+val Bouncer = SceneKey.Bouncer.toTransitionSceneKey()
+val Shade = SceneKey.Shade.toTransitionSceneKey()
+val QuickSettings = SceneKey.QuickSettings.toTransitionSceneKey()
+val Gone = SceneKey.Gone.toTransitionSceneKey()
+
+// TODO(b/293899074): Remove this file once we can use the scene keys from SceneTransitionLayout.
+fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey {
+ return SceneTransitionSceneKey(name = toString(), identity = this)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt
new file mode 100644
index 0000000..1a9face
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Bouncer
+
+fun TransitionBuilder.bouncerToGoneTransition() {
+ spec = tween(durationMillis = 500)
+
+ fade(Bouncer.rootElementKey)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt
new file mode 100644
index 0000000..38712b0
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.QuickSettings
+
+fun TransitionBuilder.goneToQuickSettingsTransition() {
+ spec = tween(durationMillis = 500)
+
+ fade(QuickSettings.rootElementKey)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
new file mode 100644
index 0000000..1d57c1a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Shade
+
+fun TransitionBuilder.goneToShadeTransition() {
+ spec = tween(durationMillis = 500)
+
+ fade(Shade.rootElementKey)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
new file mode 100644
index 0000000..1fee874
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.bouncer.ui.composable.Bouncer
+
+fun TransitionBuilder.lockscreenToBouncerTransition() {
+ spec = tween(durationMillis = 500)
+
+ translate(Bouncer.Elements.Content, y = 300.dp)
+ fractionRange(end = 0.5f) { fade(Bouncer.Elements.Background) }
+ fractionRange(start = 0.5f) { fade(Bouncer.Elements.Content) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt
new file mode 100644
index 0000000..da6306d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.Lockscreen
+
+fun TransitionBuilder.lockscreenToGoneTransition() {
+ spec = tween(durationMillis = 500)
+
+ fade(Lockscreen.rootElementKey)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt
new file mode 100644
index 0000000..9a8a3e2
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.scene.ui.composable.QuickSettings
+
+fun TransitionBuilder.lockscreenToQuickSettingsTransition() {
+ spec = tween(durationMillis = 500)
+
+ fade(QuickSettings.rootElementKey)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
new file mode 100644
index 0000000..7ecfb62
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
@@ -0,0 +1,24 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.qs.footer.ui.compose.QuickSettings
+import com.android.systemui.shade.ui.composable.Shade
+
+fun TransitionBuilder.lockscreenToShadeTransition() {
+ spec = tween(durationMillis = 500)
+
+ punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim)
+ translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false)
+ fractionRange(end = 0.5f) {
+ fade(Shade.Elements.ScrimBackground)
+ translate(
+ QuickSettings.Elements.CollapsedGrid,
+ Edge.Top,
+ startsOutsideLayoutBounds = false,
+ )
+ }
+ fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
new file mode 100644
index 0000000..6c7964b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -0,0 +1,15 @@
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.qs.footer.ui.compose.QuickSettings
+
+fun TransitionBuilder.shadeToQuickSettingsTransition() {
+ spec = tween(durationMillis = 500)
+
+ translate(Notifications.Elements.Notifications, Edge.Bottom)
+ fade(Notifications.Elements.Notifications)
+ timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index ff1cb5f..f985aa2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -16,16 +16,22 @@
package com.android.systemui.shade.ui.composable
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -44,6 +50,26 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+object Shade {
+ object Elements {
+ val QuickSettings = ElementKey("ShadeQuickSettings")
+ val Scrim = ElementKey("ShadeScrim")
+ val ScrimBackground = ElementKey("ShadeScrimBackground")
+ }
+
+ object Dimensions {
+ val ScrimCornerSize = 32.dp
+ }
+
+ object Shapes {
+ val Scrim =
+ RoundedCornerShape(
+ topStart = Dimensions.ScrimCornerSize,
+ topEnd = Dimensions.ScrimCornerSize,
+ )
+ }
+}
+
/** The shade scene shows scrolling list of notifications and some of the quick setting tiles. */
@SysUISingleton
class ShadeScene
@@ -79,20 +105,28 @@
}
@Composable
-private fun ShadeScene(
+private fun SceneScope.ShadeScene(
viewModel: ShadeSceneViewModel,
modifier: Modifier = Modifier,
) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(16.dp),
- modifier =
- modifier
- .fillMaxSize()
- .clickable(onClick = { viewModel.onContentClicked() })
- .padding(horizontal = 16.dp, vertical = 48.dp)
- ) {
- QuickSettings(modifier = Modifier.height(160.dp))
- Notifications(modifier = Modifier.weight(1f))
+ Box(modifier.element(Shade.Elements.Scrim)) {
+ Spacer(
+ modifier =
+ Modifier.element(Shade.Elements.ScrimBackground)
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim)
+ )
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ modifier =
+ Modifier.fillMaxSize()
+ .clickable(onClick = { viewModel.onContentClicked() })
+ .padding(horizontal = 16.dp, vertical = 48.dp)
+ ) {
+ QuickSettings(modifier = Modifier.height(160.dp))
+ Notifications(modifier = Modifier.weight(1f))
+ }
}
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 006fc09..92083b0 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -438,7 +438,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletControllerImpl.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -446,7 +445,6 @@
-packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarRootView.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
--packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
-packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
-packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt
-packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 12b3ec3..5c42e45 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -780,6 +780,9 @@
<!-- Flag to enable privacy dot views, it shall be true for normal case -->
<bool name="config_enablePrivacyDot">true</bool>
+ <!-- Flag to enable privacy chip animation, it shall be true for normal case -->
+ <bool name="config_enablePrivacyChipAnimation">true</bool>
+
<!-- Class for the communal source connector to be used -->
<string name="config_communalSourceConnector" translatable="false"></string>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d2cb475..4e72518 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -219,4 +219,7 @@
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
<item type="id" name="privacy_dialog_manage_app_button" />
+
+ <!-- Communal mode -->
+ <item type="id" name="communal_widget_wrapper" />
</resources>
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index cb2c3a1..2ec6180 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -60,7 +60,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintEnd_toEndOf="@id/carrier_group"/>
+ app:layout_constraintStart_toEndOf="@id/carrier_group"/>
<PropertySet android:alpha="1" />
</Constraint>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index f23ae67..4160ae1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -42,7 +42,6 @@
var keyguardIsVisible: Boolean = false,
var keyguardOccluded: Boolean = false,
var occludingAppRequestingFp: Boolean = false,
- var shouldListenSfpsState: Boolean = false,
var shouldListenForFingerprintAssistant: Boolean = false,
var strongerAuthRequired: Boolean = false,
var switchingUser: Boolean = false,
@@ -74,7 +73,6 @@
keyguardIsVisible.toString(),
keyguardOccluded.toString(),
occludingAppRequestingFp.toString(),
- shouldListenSfpsState.toString(),
shouldListenForFingerprintAssistant.toString(),
strongerAuthRequired.toString(),
switchingUser.toString(),
@@ -115,7 +113,6 @@
keyguardIsVisible = model.keyguardIsVisible
keyguardOccluded = model.keyguardOccluded
occludingAppRequestingFp = model.occludingAppRequestingFp
- shouldListenSfpsState = model.shouldListenSfpsState
shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant
strongerAuthRequired = model.strongerAuthRequired
switchingUser = model.switchingUser
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
index 96ac8ad..e1c060f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -66,11 +66,11 @@
}
override fun createAnimator(
- sceneRoot: ViewGroup?,
+ sceneRoot: ViewGroup,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
- if (sceneRoot == null || startValues == null || endValues == null) {
+ if (startValues == null || endValues == null) {
return null
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 73b4c5f..757022d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -543,7 +543,8 @@
@Nullable
@Override
- public Animator createAnimator(ViewGroup sceneRoot, @Nullable TransitionValues startValues,
+ public Animator createAnimator(@NonNull ViewGroup sceneRoot,
+ @Nullable TransitionValues startValues,
@Nullable TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 853a62a..0ba6c05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3128,18 +3128,9 @@
&& !strongerAuthRequired
&& userDoesNotHaveTrust);
- boolean shouldListenSideFpsState = true;
- if (isSideFps) {
- final boolean interactiveToAuthEnabled =
- mFingerprintInteractiveToAuthProvider != null &&
- mFingerprintInteractiveToAuthProvider.isEnabled(getCurrentUser());
- shouldListenSideFpsState =
- interactiveToAuthEnabled ? isDeviceInteractive() && !mGoingToSleep : true;
- }
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState
- && shouldListenSideFpsState;
+ && shouldListenBouncerState && shouldListenUdfpsState;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -3160,7 +3151,6 @@
isKeyguardVisible(),
mKeyguardOccluded,
mOccludingAppRequestingFp,
- shouldListenSideFpsState,
shouldListenForFingerprintAssistant,
strongerAuthRequired,
mSwitchingUser,
@@ -3314,6 +3304,9 @@
mFingerprintDetectionCallback,
new FingerprintAuthenticateOptions.Builder()
.setUserId(userId)
+ .setVendorReason(
+ mFingerprintInteractiveToAuthProvider.getVendorExtension(
+ getCurrentUser()))
.build());
} else {
mLogger.v("startListeningForFingerprint");
@@ -3322,6 +3315,9 @@
null /* handler */,
new FingerprintAuthenticateOptions.Builder()
.setUserId(userId)
+ .setVendorReason(
+ mFingerprintInteractiveToAuthProvider.getVendorExtension(
+ getCurrentUser()))
.build()
);
}
@@ -4497,14 +4493,6 @@
} else if (isSfpsSupported()) {
pw.println(" sfpsEnrolled=" + isSfpsEnrolled());
pw.println(" shouldListenForSfps=" + shouldListenForFingerprint(false));
- if (isSfpsEnrolled()) {
- final boolean interactiveToAuthEnabled =
- mFingerprintInteractiveToAuthProvider != null &&
- mFingerprintInteractiveToAuthProvider
- .isEnabled(getCurrentUser());
- pw.println(" interactiveToAuthEnabled="
- + interactiveToAuthEnabled);
- }
}
new DumpsysTableLogger(
"KeyguardFingerprintListen",
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 6ea0fc3..b33d501 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -80,7 +80,6 @@
@Override
public void start() {
- updateEnabled();
}
private void fakeWakeAndUnlock(BiometricSourceType type) {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9708d9a..340bf3f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,6 +20,7 @@
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +34,7 @@
import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import androidx.annotation.WorkerThread;
@@ -96,19 +98,58 @@
private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid =
new SparseArray<>();
- protected static final int[] OPS = new int[] {
- AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
- AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_PHONE_CALL_CAMERA,
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ @VisibleForTesting
+ protected static final int[] OPS_MIC = new int[] {
AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION
+ AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+ AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
};
+ protected static final int[] OPS_CAMERA = new int[] {
+ AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_PHONE_CALL_CAMERA
+ };
+
+ protected static final int[] OPS_LOC = new int[] {
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
+ };
+
+ protected static final int[] OPS_OTHERS = new int[] {
+ AppOpsManager.OP_SYSTEM_ALERT_WINDOW
+ };
+
+
+ protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS);
+
+ /**
+ * @param opArrays the given op arrays.
+ * @return the concatenations of the given op arrays. Null arrays are treated as empty.
+ */
+ private static int[] concatOps(@Nullable int[]...opArrays) {
+ if (opArrays == null) {
+ return new int[0];
+ }
+ int totalLength = 0;
+ for (int[] opArray : opArrays) {
+ if (opArray == null || opArray.length == 0) {
+ continue;
+ }
+ totalLength += opArray.length;
+ }
+ final int[] concatOps = new int[totalLength];
+ int index = 0;
+ for (int[] opArray : opArrays) {
+ if (opArray == null || opArray.length == 0) continue;
+ System.arraycopy(opArray, 0, concatOps, index, opArray.length);
+ index += opArray.length;
+ }
+ return concatOps;
+ }
+
@Inject
public AppOpsControllerImpl(
Context context,
@@ -533,12 +574,17 @@
}
private boolean isOpCamera(int op) {
- return op == AppOpsManager.OP_CAMERA || op == AppOpsManager.OP_PHONE_CALL_CAMERA;
+ for (int i = 0; i < OPS_CAMERA.length; i++) {
+ if (op == OPS_CAMERA[i]) return true;
+ }
+ return false;
}
private boolean isOpMicrophone(int op) {
- return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE
- || op == AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+ for (int i = 0; i < OPS_MIC.length; i++) {
+ if (op == OPS_MIC[i]) return true;
+ }
+ return false;
}
protected class H extends Handler {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 60e4cd0..7b288a8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -1049,7 +1049,7 @@
final int userId = mCurrentDialogArgs.argi1;
if (isFaceAuthEnrolled(userId) && isFingerprintEnrolled(userId)) {
messageRes = modality == TYPE_FACE
- ? R.string.biometric_face_not_recognized
+ ? R.string.fingerprint_dialog_use_fingerprint_instead
: R.string.fingerprint_error_not_match;
} else {
messageRes = R.string.biometric_not_recognized;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
index 902bb18..4bc07e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import android.hardware.biometrics.common.AuthenticateReason;
+
/** Provides the status of the interactive to auth feature. */
public interface FingerprintInteractiveToAuthProvider {
/**
@@ -24,4 +26,11 @@
* @return true if the InteractiveToAuthFeature is enabled, false if disabled.
*/
boolean isEnabled(int userId);
+
+ /**
+ *
+ * @param userId the user Id.
+ * @return Vendor extension if needed for authentication.
+ */
+ AuthenticateReason.Vendor getVendorExtension(int userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 39a45f7..b23e085 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -646,8 +646,9 @@
shouldPilfer = true;
}
- // Pilfer only once per gesture
- if (shouldPilfer && !mPointerPilfered) {
+ // Pilfer only once per gesture, don't pilfer for BP
+ if (shouldPilfer && !mPointerPilfered
+ && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
mInputManager.pilferPointers(
mOverlay.getOverlayView().getViewRootImpl().getInputToken());
mPointerPilfered = true;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 490edc6..d054751 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.binder
import android.animation.Animator
+import android.annotation.SuppressLint
import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
@@ -26,6 +27,7 @@
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.HapticFeedbackConstants
+import android.view.MotionEvent
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
import android.view.accessibility.AccessibilityManager
@@ -73,6 +75,7 @@
object BiometricViewBinder {
/** Binds a [BiometricPromptLayout] to a [PromptViewModel]. */
+ @SuppressLint("ClickableViewAccessibility")
@JvmStatic
fun bind(
view: BiometricPromptLayout,
@@ -300,21 +303,19 @@
// reuse the icon as a confirm button
launch {
- viewModel.isConfirmButtonVisible
+ viewModel.isIconConfirmButton
.map { isPending ->
when {
isPending && iconController.actsAsConfirmButton ->
- View.OnClickListener { viewModel.confirmAuthenticated() }
+ View.OnTouchListener { _: View, event: MotionEvent ->
+ viewModel.onOverlayTouch(event)
+ }
else -> null
}
}
- .collect { onClick ->
- iconViewOverlay.setOnClickListener(onClick)
- iconView.setOnClickListener(onClick)
- if (onClick == null) {
- iconViewOverlay.isClickable = false
- iconView.isClickable = false
- }
+ .collect { onTouch ->
+ iconViewOverlay.setOnTouchListener(onTouch)
+ iconView.setOnTouchListener(onTouch)
}
}
@@ -340,6 +341,14 @@
backgroundView.setOnClickListener(null)
backgroundView.importantForAccessibility =
IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Allow icon to be used as confirmation button with a11y enabled
+ if (accessibilityManager.isTouchExplorationEnabled) {
+ iconViewOverlay.setOnClickListener {
+ viewModel.confirmAuthenticated()
+ }
+ iconView.setOnClickListener { viewModel.confirmAuthenticated() }
+ }
}
if (authState.isAuthenticatedAndConfirmed) {
view.announceForAccessibility(
@@ -495,7 +504,7 @@
modalities.hasFaceAndFingerprint &&
(viewModel.fingerprintStartMode.first() != FingerprintStartMode.Pending) &&
(authenticatedModality == BiometricModality.Face) ->
- R.string.biometric_dialog_tap_confirm_with_face
+ R.string.biometric_dialog_tap_confirm_with_face_alt_1
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 655e74a..89561a5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -18,6 +18,7 @@
import android.hardware.biometrics.BiometricPrompt
import android.util.Log
import android.view.HapticFeedbackConstants
+import android.view.MotionEvent
import com.android.systemui.biometrics.AuthBiometricView
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.model.BiometricModalities
@@ -67,11 +68,18 @@
/** If the user has successfully authenticated and confirmed (when explicitly required). */
val isAuthenticated: Flow<PromptAuthState> = _isAuthenticated.asStateFlow()
+ private val _isOverlayTouched: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
/**
* If the API caller or the user's personal preferences require explicit confirmation after
* successful authentication.
*/
- val isConfirmationRequired: Flow<Boolean> = interactor.isConfirmationRequired
+ val isConfirmationRequired: Flow<Boolean> =
+ combine(_isOverlayTouched, interactor.isConfirmationRequired) {
+ isOverlayTouched,
+ isConfirmationRequired ->
+ !isOverlayTouched && isConfirmationRequired
+ }
/** The kind of credential the user has. */
val credentialKind: Flow<PromptKind> = interactor.credentialKind
@@ -150,6 +158,12 @@
}
.distinctUntilChanged()
+ /** If the icon can be used as a confirmation button. */
+ val isIconConfirmButton: Flow<Boolean> =
+ combine(size, interactor.isConfirmationRequired) { size, isConfirmationRequired ->
+ size.isNotSmall && isConfirmationRequired
+ }
+
/** If the negative button should be shown. */
val isNegativeButtonVisible: Flow<Boolean> =
combine(
@@ -298,8 +312,10 @@
if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty
_forceMediumSize.value = true
_legacyState.value =
- if (alreadyAuthenticated) {
+ if (alreadyAuthenticated && isConfirmationRequired.first()) {
AuthBiometricView.STATE_PENDING_CONFIRMATION
+ } else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
+ AuthBiometricView.STATE_AUTHENTICATED
} else {
AuthBiometricView.STATE_HELP
}
@@ -397,18 +413,10 @@
}
private suspend fun needsExplicitConfirmation(modality: BiometricModality): Boolean {
- val availableModalities = modalities.first()
val confirmationRequired = isConfirmationRequired.first()
- if (availableModalities.hasFaceAndFingerprint) {
- // coex only needs confirmation when face is successful, unless it happens on the
- // first attempt (i.e. without failure) before fingerprint scanning starts
- val fingerprintStarted = fingerprintStartMode.first() != FingerprintStartMode.Pending
- if (modality == BiometricModality.Face) {
- return fingerprintStarted || confirmationRequired
- }
- }
- if (availableModalities.hasFaceOnly) {
+ // Only worry about confirmationRequired if face was used to unlock
+ if (modality == BiometricModality.Face) {
return confirmationRequired
}
// fingerprint only never requires confirmation
@@ -439,6 +447,26 @@
}
/**
+ * Touch event occurred on the overlay
+ *
+ * Tracks whether a finger is currently down to set [_isOverlayTouched] to be used as user
+ * confirmation
+ */
+ fun onOverlayTouch(event: MotionEvent): Boolean {
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
+ _isOverlayTouched.value = true
+
+ if (_isAuthenticated.value.needsUserConfirmation) {
+ confirmAuthenticated()
+ }
+ return true
+ } else if (event.actionMasked == MotionEvent.ACTION_UP) {
+ _isOverlayTouched.value = false
+ }
+ return false
+ }
+
+ /**
* Switch to the credential view.
*
* TODO(b/251476085): this should be decoupled from the shared panel controller
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt
index 0b8e83e..1b45ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt
@@ -59,7 +59,7 @@
context.startActivity(intent, transition.first.toBundle())
val runner = RemoteAnimationAdapter(NULL_ACTIVITY_TRANSITION, 0, 0)
try {
- WindowManagerGlobal.getWindowManagerService()
+ checkNotNull(WindowManagerGlobal.getWindowManagerService())
.overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId)
} catch (e: Exception) {
Log.e(TAG, "Error overriding clipboard app transition", e)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
new file mode 100644
index 0000000..e2a7d07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.os.UserManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates the state of widgets for communal mode. */
+interface CommunalWidgetRepository {
+ /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
+ val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
+}
+
+@SysUISingleton
+class CommunalWidgetRepositoryImpl
+@Inject
+constructor(
+ private val appWidgetManager: AppWidgetManager,
+ private val appWidgetHost: AppWidgetHost,
+ broadcastDispatcher: BroadcastDispatcher,
+ private val packageManager: PackageManager,
+ private val userManager: UserManager,
+ private val userTracker: UserTracker,
+ @CommunalLog logBuffer: LogBuffer,
+ featureFlags: FeatureFlags,
+) : CommunalWidgetRepository {
+ companion object {
+ const val TAG = "CommunalWidgetRepository"
+ const val WIDGET_LABEL = "Stopwatch"
+ }
+
+ private val logger = Logger(logBuffer, TAG)
+
+ // Whether the [AppWidgetHost] is listening for updates.
+ private var isHostListening = false
+
+ // Widgets that should be rendered in communal mode.
+ private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
+
+ private val isUserUnlocked: Flow<Boolean> = callbackFlow {
+ if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+ awaitClose()
+ }
+
+ fun isUserUnlockingOrUnlocked(): Boolean {
+ return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle)
+ }
+
+ fun send() {
+ trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG)
+ }
+
+ if (isUserUnlockingOrUnlocked()) {
+ send()
+ awaitClose()
+ } else {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ send()
+ }
+ }
+
+ broadcastDispatcher.registerReceiver(
+ receiver,
+ IntentFilter(Intent.ACTION_USER_UNLOCKED),
+ )
+
+ awaitClose { broadcastDispatcher.unregisterReceiver(receiver) }
+ }
+ }
+
+ override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
+ isUserUnlocked.map { isUserUnlocked ->
+ if (!isUserUnlocked) {
+ clearWidgets()
+ stopListening()
+ return@map null
+ }
+
+ startListening()
+
+ val providerInfo =
+ appWidgetManager.installedProviders.find {
+ it.loadLabel(packageManager).equals(WIDGET_LABEL)
+ }
+
+ if (providerInfo == null) {
+ logger.w("Cannot find app widget: $WIDGET_LABEL")
+ return@map null
+ }
+
+ return@map addWidget(providerInfo)
+ }
+
+ private fun startListening() {
+ if (isHostListening) {
+ return
+ }
+
+ appWidgetHost.startListening()
+ isHostListening = true
+ }
+
+ private fun stopListening() {
+ if (!isHostListening) {
+ return
+ }
+
+ appWidgetHost.stopListening()
+ isHostListening = false
+ }
+
+ private fun addWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
+ val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
+ if (existing != null) {
+ return existing
+ }
+
+ val appWidgetId = appWidgetHost.allocateAppWidgetId()
+ val widget =
+ CommunalAppWidgetInfo(
+ providerInfo,
+ appWidgetId,
+ )
+ widgets[appWidgetId] = widget
+ return widget
+ }
+
+ private fun clearWidgets() {
+ widgets.keys.forEach { appWidgetId -> appWidgetHost.deleteAppWidgetId(appWidgetId) }
+ widgets.clear()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
new file mode 100644
index 0000000..3d1185b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface CommunalWidgetRepositoryModule {
+ companion object {
+ private const val APP_WIDGET_HOST_ID = 116
+
+ @SysUISingleton
+ @Provides
+ fun provideAppWidgetManager(@Application context: Context): AppWidgetManager {
+ return AppWidgetManager.getInstance(context)
+ }
+
+ @SysUISingleton
+ @Provides
+ fun provideAppWidgetHost(@Application context: Context): AppWidgetHost {
+ return AppWidgetHost(context, APP_WIDGET_HOST_ID)
+ }
+ }
+
+ @Binds
+ fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
new file mode 100644
index 0000000..6dc305e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates business-logic related to communal mode. */
+@SysUISingleton
+class CommunalInteractor
+@Inject
+constructor(
+ widgetRepository: CommunalWidgetRepository,
+) {
+ /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
+ val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
new file mode 100644
index 0000000..0803a01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.shared
+
+import android.appwidget.AppWidgetProviderInfo
+
+/** A data class that stores info about an app widget that displays in communal mode. */
+data class CommunalAppWidgetInfo(
+ val providerInfo: AppWidgetProviderInfo,
+ val appWidgetId: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
new file mode 100644
index 0000000..2a08d7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.adapter
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import android.util.SizeF
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Transforms a [CommunalAppWidgetInfo] to a view that renders the widget. */
+class CommunalWidgetViewAdapter
+@Inject
+constructor(
+ @Application private val context: Context,
+ private val appWidgetManager: AppWidgetManager,
+ private val appWidgetHost: AppWidgetHost,
+ @CommunalLog logBuffer: LogBuffer,
+) {
+ companion object {
+ private const val TAG = "CommunalWidgetViewAdapter"
+ }
+
+ private val logger = Logger(logBuffer, TAG)
+
+ fun adapt(providerInfoFlow: Flow<CommunalAppWidgetInfo?>): Flow<CommunalWidgetWrapper?> =
+ providerInfoFlow.map {
+ if (it == null) {
+ return@map null
+ }
+
+ val appWidgetId = it.appWidgetId
+ val providerInfo = it.providerInfo
+
+ if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, providerInfo.provider)) {
+ logger.d("Success binding app widget id: $appWidgetId")
+ return@map CommunalWidgetWrapper(context).apply {
+ addView(
+ appWidgetHost.createView(context, appWidgetId, providerInfo).apply {
+ // Set the widget to minimum width and height
+ updateAppWidgetSize(
+ appWidgetManager.getAppWidgetOptions(appWidgetId),
+ listOf(
+ SizeF(
+ providerInfo.minResizeWidth.toFloat(),
+ providerInfo.minResizeHeight.toFloat()
+ )
+ )
+ )
+ }
+ )
+ }
+ } else {
+ logger.w("Failed binding app widget id")
+ return@map null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
new file mode 100644
index 0000000..1b6d3a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+/** Binds [CommunalWidgetViewModel] to the keyguard root view. */
+object CommunalWidgetViewBinder {
+
+ @JvmStatic
+ fun bind(
+ rootView: KeyguardRootView,
+ viewModel: CommunalWidgetViewModel,
+ adapter: CommunalWidgetViewAdapter,
+ keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+ ) {
+ rootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ adapter.adapt(viewModel.appWidgetInfo).collect {
+ val oldView =
+ rootView.findViewById<CommunalWidgetWrapper>(
+ R.id.communal_widget_wrapper
+ )
+ var dirty = false
+
+ if (oldView != null) {
+ rootView.removeView(oldView)
+ dirty = true
+ }
+
+ if (it != null) {
+ rootView.addView(it)
+ dirty = true
+ }
+
+ if (dirty) {
+ keyguardBlueprintInteractor.refreshBlueprint()
+ }
+ }
+ }
+
+ launch { viewModel.alpha.collect { rootView.alpha = it } }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
new file mode 100644
index 0000000..560f4fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
@@ -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.systemui.communal.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+/** Wraps around a widget rendered in communal mode. */
+class CommunalWidgetWrapper(context: Context, attrs: AttributeSet? = null) :
+ LinearLayout(context, attrs) {
+ init {
+ id = R.id.communal_widget_wrapper
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
new file mode 100644
index 0000000..c3369da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.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.systemui.communal.ui.view.layout.blueprints
+
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import javax.inject.Inject
+
+/** Blueprint for communal mode. */
+@SysUISingleton
+@JvmSuppressWildcards
+class DefaultCommunalBlueprint
+@Inject
+constructor(
+ private val defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
+) : KeyguardBlueprint {
+ override val id: String = COMMUNAL
+
+ override fun apply(constraintSet: ConstraintSet) {
+ defaultCommunalWidgetSection.apply(constraintSet)
+ }
+
+ companion object {
+ const val COMMUNAL = "communal"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
new file mode 100644
index 0000000..b0e3132
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.view.layout.sections
+
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import com.android.systemui.R
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection {
+ private val widgetAreaViewId = R.id.communal_widget_wrapper
+
+ override fun apply(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ constrainWidth(widgetAreaViewId, WRAP_CONTENT)
+ constrainHeight(widgetAreaViewId, WRAP_CONTENT)
+ connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM)
+ connect(widgetAreaViewId, END, PARENT_ID, END)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
new file mode 100644
index 0000000..8fba342
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class CommunalWidgetViewModel
+@Inject
+constructor(
+ communalInteractor: CommunalInteractor,
+ keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
+) {
+ /** An observable for the alpha level for the communal widget area. */
+ val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+
+ /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
+ val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 58d606e..4a22a67 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -391,13 +391,13 @@
// TODO(b/293863612): Tracking Bug
@JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
- unreleasedFlag("incompatible_charging_battery_icon")
+ releasedFlag("incompatible_charging_battery_icon")
// TODO(b/293585143): Tracking Bug
val INSTANT_TETHER = unreleasedFlag("instant_tether")
// TODO(b/294588085): Tracking Bug
- val WIFI_SECONDARY_NETWORKS = unreleasedFlag("wifi_secondary_networks")
+ val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 8e323d8..9b323ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -24,9 +24,13 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.R
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
@@ -83,6 +87,9 @@
private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+ private val communalWidgetViewModel: CommunalWidgetViewModel,
+ private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -106,6 +113,7 @@
bindRightShortcut()
bindAmbientIndicationArea()
bindSettingsPopupMenu()
+ bindCommunalWidgetArea()
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
@@ -279,6 +287,19 @@
}
}
+ private fun bindCommunalWidgetArea() {
+ if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+ return
+ }
+
+ CommunalWidgetViewBinder.bind(
+ keyguardRootView,
+ communalWidgetViewModel,
+ communalWidgetViewAdapter,
+ keyguardBlueprintInteractor,
+ )
+ }
+
/**
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9a44230..13dfe24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -75,11 +76,12 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
-import java.util.concurrent.Executor;
-
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+
+import java.util.concurrent.Executor;
+
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -91,6 +93,7 @@
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
+ CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index fefe679..07f316b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint
import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
import dagger.Binds
import dagger.Module
@@ -35,4 +36,10 @@
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
+
+ @Binds
+ @IntoSet
+ abstract fun bindDefaultCommunalBlueprint(
+ defaultCommunalBlueprint: DefaultCommunalBlueprint
+ ): KeyguardBlueprint
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt
new file mode 100644
index 0000000..afb18f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for communal-related logging. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class CommunalLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index b6577f7..5127d14 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -497,6 +497,16 @@
return factory.create("DreamLog", 250);
}
+ /**
+ * Provides a {@link LogBuffer} for communal-related logs.
+ */
+ @Provides
+ @SysUISingleton
+ @CommunalLog
+ public static LogBuffer provideCommunalLogBuffer(LogBufferFactory factory) {
+ return factory.create("CommunalLog", 250);
+ }
+
/** Provides a {@link LogBuffer} for display metrics related logs. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 39d4e6e..412d1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -56,6 +56,7 @@
* Update the endpoints of a content switching operation.
* This method should be called before a switching operation, so the metric logger can track
* source and target devices.
+ *
* @param source the current connected media device
* @param target the target media device for content switching to
*/
@@ -72,37 +73,9 @@
/**
* Do the metric logging of content switching success.
+ *
* @param selectedDeviceType string representation of the target media device
- * @param deviceList media device list for device count updating
- */
- public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) {
- if (DEBUG) {
- Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
- }
-
- if (mSourceDevice == null && mTargetDevice == null) {
- return;
- }
-
- updateLoggingDeviceCount(deviceList);
-
- SysUiStatsLog.write(
- SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
- getLoggingDeviceType(mSourceDevice, true),
- getLoggingDeviceType(mTargetDevice, false),
- SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK,
- SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR,
- getLoggingPackageName(),
- mWiredDeviceCount,
- mConnectedBluetoothDeviceCount,
- mRemoteDeviceCount,
- mAppliedDeviceCountWithinRemoteGroup);
- }
-
- /**
- * Do the metric logging of content switching success.
- * @param selectedDeviceType string representation of the target media device
- * @param deviceItemList media item list for device count updating
+ * @param deviceItemList media item list for device count updating
*/
public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) {
if (DEBUG) {
@@ -125,11 +98,14 @@
mWiredDeviceCount,
mConnectedBluetoothDeviceCount,
mRemoteDeviceCount,
- mAppliedDeviceCountWithinRemoteGroup);
+ mAppliedDeviceCountWithinRemoteGroup,
+ mTargetDevice.isSuggestedDevice(),
+ mTargetDevice.hasOngoingSession());
}
/**
* Do the metric logging of volume adjustment.
+ *
* @param source the device been adjusted
*/
public void logInteractionAdjustVolume(MediaDevice source) {
@@ -141,7 +117,8 @@
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME,
getInteractionDeviceType(source),
- getLoggingPackageName());
+ getLoggingPackageName(),
+ source.isSuggestedDevice());
}
/**
@@ -156,7 +133,8 @@
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE,
- getLoggingPackageName());
+ getLoggingPackageName(),
+ /*isSuggestedDevice = */false);
}
/**
@@ -171,42 +149,15 @@
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION,
getInteractionDeviceType(source),
- getLoggingPackageName());
- }
-
- /**
- * Do the metric logging of content switching failure.
- * @param deviceList media device list for device count updating
- * @param reason the reason of content switching failure
- */
- public void logOutputFailure(List<MediaDevice> deviceList, int reason) {
- if (DEBUG) {
- Log.e(TAG, "logRequestFailed - " + reason);
- }
-
- if (mSourceDevice == null && mTargetDevice == null) {
- return;
- }
-
- updateLoggingDeviceCount(deviceList);
-
- SysUiStatsLog.write(
- SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
- getLoggingDeviceType(mSourceDevice, true),
- getLoggingDeviceType(mTargetDevice, false),
- SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR,
- getLoggingSwitchOpSubResult(reason),
getLoggingPackageName(),
- mWiredDeviceCount,
- mConnectedBluetoothDeviceCount,
- mRemoteDeviceCount,
- mAppliedDeviceCountWithinRemoteGroup);
+ source.isSuggestedDevice());
}
/**
* Do the metric logging of content switching failure.
+ *
* @param deviceItemList media item list for device count updating
- * @param reason the reason of content switching failure
+ * @param reason the reason of content switching failure
*/
public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) {
if (DEBUG) {
@@ -229,7 +180,9 @@
mWiredDeviceCount,
mConnectedBluetoothDeviceCount,
mRemoteDeviceCount,
- mAppliedDeviceCountWithinRemoteGroup);
+ mAppliedDeviceCountWithinRemoteGroup,
+ mTargetDevice.isSuggestedDevice(),
+ mTargetDevice.hasOngoingSession());
}
private void updateLoggingDeviceCount(List<MediaDevice> deviceList) {
@@ -266,7 +219,7 @@
mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
mAppliedDeviceCountWithinRemoteGroup = 0;
- for (MediaItem mediaItem: deviceItemList) {
+ for (MediaItem mediaItem : deviceItemList) {
if (mediaItem.getMediaDevice().isPresent()
&& mediaItem.getMediaDevice().get().isConnected()) {
switch (mediaItem.getMediaDevice().get().getDeviceType()) {
@@ -326,6 +279,10 @@
return isSourceDevice
? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP
: SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP;
+ case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+ return isSourceDevice
+ ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR
+ : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR;
default:
return isSourceDevice
? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
index 88b8676..fedbdec 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
@@ -53,11 +53,14 @@
@VisibleForTesting
companion object {
- val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
+ val OPS_MIC_CAMERA = intArrayOf(
+ AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_PHONE_CALL_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO)
+ AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
+ AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO)
val OPS_LOCATION = intArrayOf(
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
@@ -212,6 +215,7 @@
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
+ AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING
index 2d45c5b2..86ef7ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING
+++ b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING
@@ -10,6 +10,17 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "QuickSettingsDeviceResetTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index a162d11..18f59b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -20,6 +20,7 @@
import android.content.res.Resources
import android.database.ContentObserver
import android.provider.Settings
+import android.util.SparseArray
import com.android.systemui.R
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -107,6 +108,7 @@
) : TileSpecRepository {
private val mutex = Mutex()
+ private val tileSpecsPerUser = SparseArray<List<TileSpec>>()
private val retailModeTiles by lazy {
resources
@@ -142,10 +144,12 @@
awaitClose { secureSettings.unregisterContentObserver(observer) }
}
.onStart { emit(Unit) }
- .map { secureSettings.getStringForUser(SETTING, userId) ?: "" }
- .distinctUntilChanged()
+ .map { loadTiles(userId) }
.onEach { logger.logTilesChangedInSettings(it, userId) }
- .map { parseTileSpecs(it, userId) }
+ .distinctUntilChanged()
+ .map { parseTileSpecs(it, userId).also { storeTiles(userId, it) } }
+ .distinctUntilChanged()
+ .onEach { mutex.withLock { tileSpecsPerUser.put(userId, it) } }
.flowOn(backgroundDispatcher)
}
@@ -154,7 +158,7 @@
if (tile == TileSpec.Invalid) {
return
}
- val tilesList = loadTiles(userId).toMutableList()
+ val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
if (tile !in tilesList) {
if (position < 0 || position >= tilesList.size) {
tilesList.add(tile)
@@ -162,6 +166,7 @@
tilesList.add(position, tile)
}
storeTiles(userId, tilesList)
+ tileSpecsPerUser.put(userId, tilesList)
}
}
@@ -170,9 +175,10 @@
if (tiles.all { it == TileSpec.Invalid }) {
return
}
- val tilesList = loadTiles(userId).toMutableList()
+ val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
if (tilesList.removeAll(tiles)) {
storeTiles(userId, tilesList.toList())
+ tileSpecsPerUser.put(userId, tilesList)
}
}
@@ -181,18 +187,10 @@
val filtered = tiles.filter { it != TileSpec.Invalid }
if (filtered.isNotEmpty()) {
storeTiles(userId, filtered)
+ tileSpecsPerUser.put(userId, tiles)
}
}
- private suspend fun loadTiles(@UserIdInt forUser: Int): List<TileSpec> {
- return withContext(backgroundDispatcher) {
- (secureSettings.getStringForUser(SETTING, forUser) ?: "")
- .split(DELIMITER)
- .map(TileSpec::create)
- .filter { it !is TileSpec.Invalid }
- }
- }
-
private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
if (retailModeRepository.inRetailMode) {
// No storing tiles when in retail mode
@@ -214,6 +212,12 @@
}
}
+ private suspend fun loadTiles(userId: Int): String {
+ return withContext(backgroundDispatcher) {
+ secureSettings.getStringForUser(SETTING, userId) ?: ""
+ }
+ }
+
private fun parseTileSpecs(tilesFromSettings: String, user: Int): List<TileSpec> {
val fromSettings =
tilesFromSettings.split(DELIMITER).map(TileSpec::create).filter {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index ff881f7..966f370 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -56,6 +56,8 @@
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -273,7 +275,9 @@
}
override fun addTile(spec: TileSpec, position: Int) {
- scope.launch {
+ scope.launch(backgroundDispatcher) {
+ // Block until the list is not empty
+ currentTiles.filter { it.isNotEmpty() }.first()
tileSpecRepository.addTile(userRepository.getSelectedUserInfo().id, spec, position)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index ecd4568..aa6bfc3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -83,7 +83,7 @@
if (overrideTransition) {
val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
try {
- WindowManagerGlobal.getWindowManagerService()
+ checkNotNull(WindowManagerGlobal.getWindowManagerService())
.overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId)
} catch (e: Exception) {
Log.e(TAG, "Error overriding screenshot app transition", e)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d0cbb1b..132cd61 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2131,6 +2131,7 @@
}
updateExpansionAndVisibility();
mNotificationStackScrollLayoutController.setPanelFlinging(false);
+ mShadeLog.d("onFlingEnd called"); // TODO(b/277909752): remove log when bug is fixed
// expandImmediate should be always reset at the end of animation
mQsController.setExpandImmediate(false);
}
@@ -2666,6 +2667,11 @@
setListening(true);
}
if (mBarState != SHADE) {
+ // TODO(b/277909752): remove below logs when bug is fixed
+ mShadeLog.d("onExpandingFinished called");
+ if (mSplitShadeEnabled && !mQsController.getExpanded()) {
+ mShadeLog.d("onExpandingFinished called before QS got expanded");
+ }
// updating qsExpandImmediate is done in onPanelStateChanged for unlocked shade but
// on keyguard panel state is always OPEN so we need to have that extra update
mQsController.setExpandImmediate(false);
@@ -4718,6 +4724,7 @@
}
private void onPanelStateChanged(@PanelState int state) {
+ mShadeLog.logPanelStateChanged(state);
mQsController.updateExpansionEnabledAmbient();
if (state == STATE_OPEN && mCurrentPanelState != state) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index e85024e..c9c911b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -787,6 +787,12 @@
/** update Qs height state */
public void setExpansionHeight(float height) {
+ // TODO(b/277909752): remove below log when bug is fixed
+ if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0) {
+ Log.wtf(TAG,
+ "setting QS height to 0 in split shade while shade is open(ing). "
+ + "Value of mExpandImmediate = " + mExpandImmediate);
+ }
int maxHeight = getMaxExpansionHeight();
height = Math.min(Math.max(
height, getMinExpansionHeight()), maxHeight);
@@ -933,7 +939,6 @@
return mShadeExpandedHeight;
}
- @VisibleForTesting
void setExpandImmediate(boolean expandImmediate) {
if (expandImmediate != mExpandImmediate) {
mShadeLog.logQsExpandImmediateChanged(expandImmediate);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index c3ef925..8d23f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -336,6 +336,17 @@
)
}
+ fun logPanelStateChanged(@PanelState panelState: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = panelState.panelStateToString()
+ },
+ { "New panel State: $str1" }
+ )
+ }
+
fun flingQs(flingType: Int, isClick: Boolean) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/TouchLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/TouchLogger.kt
index d96dc12..58704bf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/TouchLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/TouchLogger.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade
import android.view.MotionEvent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index 23edf17..2403920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -164,7 +164,9 @@
}
private fun isChipAnimationEnabled(): Boolean {
- return DeviceConfig.getBoolean(NAMESPACE_PRIVACY, CHIP_ANIMATION_ENABLED, true)
+ val defaultValue =
+ context.resources.getBoolean(R.bool.config_enablePrivacyChipAnimation)
+ return DeviceConfig.getBoolean(NAMESPACE_PRIVACY, CHIP_ANIMATION_ENABLED, defaultValue)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index e018397..f40f570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -243,7 +243,7 @@
if (!event.showAnimation && event.forceVisible) {
// If animations are turned off, we'll transition directly to the dot
animationState.value = SHOWING_PERSISTENT_DOT
- notifyTransitionToPersistentDot()
+ notifyTransitionToPersistentDot(event)
return
}
@@ -335,7 +335,7 @@
}
animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot))
if (hasPersistentDot) {
- val dotAnim = notifyTransitionToPersistentDot()
+ val dotAnim = notifyTransitionToPersistentDot(currentlyDisplayedEvent)
if (dotAnim != null) {
animators.add(dotAnim)
}
@@ -344,12 +344,12 @@
return AnimatorSet().also { it.playTogether(animators) }
}
- private fun notifyTransitionToPersistentDot(): Animator? {
+ private fun notifyTransitionToPersistentDot(event: StatusEvent?): Animator? {
logger?.logTransitionToPersistentDotCallbackInvoked()
val anims: List<Animator> =
listeners.mapNotNull {
it.onSystemStatusAnimationTransitionToPersistentDot(
- currentlyDisplayedEvent?.contentDescription
+ event?.contentDescription
)
}
if (anims.isNotEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index f026f0f..bfed0c4 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -227,7 +227,8 @@
)
}
try {
- WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null)
+ checkNotNull(WindowManagerGlobal.getWindowManagerService())
+ .lockNow(/* options= */ null)
} catch (e: RemoteException) {
Log.e(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 316b54e..9dca013 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -133,6 +133,7 @@
setShowForAllUsers(true);
mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor(
mLongExecutor,
+ mLock,
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
@Override
public void onColorsProcessed(List<RectF> regions,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index 1e8446f..e2ec8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -61,7 +61,7 @@
private int mBitmapWidth = -1;
private int mBitmapHeight = -1;
- private final Object mLock = new Object();
+ private final Object mLock;
private final List<RectF> mPendingRegions = new ArrayList<>();
private final Set<RectF> mProcessedRegions = new ArraySet<>();
@@ -102,12 +102,15 @@
/**
* Creates a new color extractor.
* @param longExecutor the executor on which the color extraction will be performed
+ * @param lock the lock object to use for computing colors or processing the bitmap
* @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from
* the color extractor.
*/
public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor,
+ Object lock,
WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) {
mLongExecutor = longExecutor;
+ mLock = lock;
mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback;
}
@@ -149,6 +152,12 @@
private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
synchronized (mLock) {
+ if (bitmap.isRecycled()) {
+ // ImageWallpaper loaded a new bitmap before the extraction of the previous one
+ // In that case, ImageWallpaper also scheduled the extraction of the next bitmap
+ Log.i(TAG, "Wallpaper has changed; deferring color extraction");
+ return;
+ }
if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
Log.e(TAG, "Attempt to extract colors from an invalid bitmap");
return;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index cb18229..9f7ab7b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -150,7 +150,7 @@
private fun getPatternTopGuideline(): Float {
val cs = ConstraintSet()
val container =
- mKeyguardPatternView.findViewById(R.id.pattern_container) as ConstraintLayout
+ mKeyguardPatternView.requireViewById(R.id.pattern_container) as ConstraintLayout
cs.clone(container)
return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 4dc7652..309d9e0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -114,7 +114,7 @@
objectKeyguardPINView =
View.inflate(mContext, R.layout.keyguard_pin_view, null)
- .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
+ .requireViewById(R.id.keyguard_pin_view) as KeyguardPINView
}
private fun constructPinViewController(
@@ -175,7 +175,7 @@
private fun getPinTopGuideline(): Float {
val cs = ConstraintSet()
- val container = objectKeyguardPINView.findViewById(R.id.pin_container) as ConstraintLayout
+ val container = objectKeyguardPINView.requireViewById(R.id.pin_container) as ConstraintLayout
cs.clone(container)
return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index f8262d4..210f3cb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -21,9 +21,9 @@
private lateinit var keyguardStatusView: KeyguardStatusView
private val mediaView: View
- get() = keyguardStatusView.findViewById(R.id.status_view_media_container)
+ get() = keyguardStatusView.requireViewById(R.id.status_view_media_container)
private val statusViewContainer: ViewGroup
- get() = keyguardStatusView.findViewById(R.id.status_view_container)
+ get() = keyguardStatusView.requireViewById(R.id.status_view_container)
private val childrenExcludingMedia
get() = statusViewContainer.children.filter { it != mediaView }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6f3322a..0cd82f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1694,71 +1694,6 @@
}
@Test
- public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
- throws RemoteException {
- // SFPS supported and enrolled
- when(mAuthController.isSfpsSupported()).thenReturn(true);
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-
- // WHEN require interactive to auth is disabled, and keyguard is not awake
- when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(false);
-
- // Preconditions for sfps auth to run
- keyguardNotGoingAway();
- currentUserIsSystem();
- currentUserDoesNotHaveTrust();
- biometricsNotDisabledThroughDevicePolicyManager();
- biometricsEnabledForCurrentUser();
- userNotCurrentlySwitching();
-
- statusBarShadeIsLocked();
- mTestableLooper.processAllMessages();
-
- // THEN we should listen for sfps when screen off, because require screen on is disabled
- assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
-
- // WHEN require interactive to auth is enabled, and keyguard is not awake
- when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
-
- // THEN we shouldn't listen for sfps when screen off, because require screen on is enabled
- assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
-
- // Device now awake & keyguard is now interactive
- deviceNotGoingToSleep();
- deviceIsInteractive();
- keyguardIsVisible();
-
- // THEN we should listen for sfps when screen on, and require screen on is enabled
- assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
- }
-
- @Test
- public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
- throws RemoteException {
- // GIVEN SFPS supported and enrolled
- when(mAuthController.isSfpsSupported()).thenReturn(true);
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-
- // GIVEN Preconditions for sfps auth to run
- keyguardNotGoingAway();
- currentUserIsSystem();
- currentUserDoesNotHaveTrust();
- biometricsNotDisabledThroughDevicePolicyManager();
- biometricsEnabledForCurrentUser();
- userNotCurrentlySwitching();
- statusBarShadeIsLocked();
-
- // WHEN require interactive to auth is enabled & keyguard is going to sleep
- when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
- deviceGoingToSleep();
-
- mTestableLooper.processAllMessages();
-
- // THEN we should NOT listen for sfps because device is going to sleep
- assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
- }
-
- @Test
public void listeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthDisabled()
throws RemoteException {
// GIVEN SFPS supported and enrolled
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
index 6fe8892..9f9b9a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
@@ -19,9 +19,11 @@
import android.testing.AndroidTestingRunner
import android.transition.TransitionValues
import android.view.View
+import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardStatusViewController.SplitShadeTransitionAdapter
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -84,5 +86,5 @@
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
- return createAnimator(/* sceneRoot= */ null, startValues, endValues)
+ return createAnimator(/* sceneRoot= */ mock(), startValues, endValues)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 316de59..2233e322 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -70,7 +70,7 @@
assertTrue(dialog.isShowing)
// The dialog is now fullscreen.
- val window = dialog.window
+ val window = checkNotNull(dialog.window)
val decorView = window.decorView as DecorView
assertEquals(MATCH_PARENT, window.attributes.width)
assertEquals(MATCH_PARENT, window.attributes.height)
@@ -172,14 +172,15 @@
// Important: the power menu animation relies on this behavior to know when to animate (see
// http://ag/16774605).
val dialog = runOnMainThreadAndWaitForIdleSync { TestDialog(context) }
- dialog.window.setWindowAnimations(0)
- assertEquals(0, dialog.window.attributes.windowAnimations)
+ val window = checkNotNull(dialog.window)
+ window.setWindowAnimations(0)
+ assertEquals(0, window.attributes.windowAnimations)
val touchSurface = createTouchSurface()
runOnMainThreadAndWaitForIdleSync {
dialogLaunchAnimator.showFromView(dialog, touchSurface)
}
- assertNotEquals(0, dialog.window.attributes.windowAnimations)
+ assertNotEquals(0, window.attributes.windowAnimations)
}
@Test
@@ -351,13 +352,14 @@
init {
// We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
- window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ checkNotNull(window).setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(contentView)
+ val window = checkNotNull(window)
window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
window.setBackgroundDrawable(windowBackground)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 61a6512..b23f7f2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -19,6 +19,8 @@
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+import static com.android.systemui.appops.AppOpsControllerImpl.OPS_MIC;
+
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
@@ -171,6 +173,28 @@
TEST_UID, TEST_PACKAGE_NAME, true);
}
+
+ // Only the app ops in the {@link com.android.systemui.appops.AppOpsControllerImpl.OPS} will be
+ // supported by the {@link AppOpsControllerImpl} to add callbacks. The state changes of active
+ // app ops will be notified by the callback.
+ @Test
+ public void addCallback_partialIncludedCode() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mController.onOpActiveChanged(
+ AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ mController.onOpActiveChanged(
+ AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, TEST_UID, TEST_PACKAGE_NAME,
+ true);
+ assertEquals(2, mController.getActiveAppOps().size());
+
+ mTestableLooper.processAllMessages();
+ verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ TEST_UID, TEST_PACKAGE_NAME, true);
+ verify(mCallback, never()).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+
@Test
public void addCallback_notIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
@@ -504,41 +528,17 @@
}
@Test
- public void testUnpausedRecordingSentActive() {
- mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
- mTestableLooper.processAllMessages();
- mController.onOpActiveChanged(
- AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-
- mTestableLooper.processAllMessages();
- mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
-
- mTestableLooper.processAllMessages();
-
- verify(mCallback).onActiveStateChanged(
- AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ public void testUnPausedRecordingSentActive() {
+ for (int i = 0; i < OPS_MIC.length; i++) {
+ verifyUnPausedSentActive(OPS_MIC[i]);
+ }
}
@Test
public void testAudioPausedSentInactive() {
- mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
- mTestableLooper.processAllMessages();
- mController.onOpActiveChanged(
- AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
- mTestableLooper.processAllMessages();
-
- AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
- when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER);
- when(mockARC.isClientSilenced()).thenReturn(true);
-
- mRecordingCallback.onRecordingConfigChanged(List.of(mockARC));
- mTestableLooper.processAllMessages();
-
- InOrder inOrder = inOrder(mCallback);
- inOrder.verify(mCallback).onActiveStateChanged(
- AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
- inOrder.verify(mCallback).onActiveStateChanged(
- AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
+ for (int i = 0; i < OPS_MIC.length; i++) {
+ verifyAudioPausedSentInactive(OPS_MIC[i]);
+ }
}
@Test
@@ -673,6 +673,41 @@
assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode());
}
+ private void verifyUnPausedSentActive(int micOpCode) {
+ mController.addCallback(new int[]{micOpCode}, mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID,
+ TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+ mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback).onActiveStateChanged(micOpCode, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+
+ private void verifyAudioPausedSentInactive(int micOpCode) {
+ mController.addCallback(new int[]{micOpCode}, mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID_OTHER,
+ TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+
+ AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
+ when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER);
+ when(mockARC.isClientSilenced()).thenReturn(true);
+
+ mRecordingCallback.onRecordingConfigChanged(List.of(mockARC));
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mCallback);
+ inOrder.verify(mCallback).onActiveStateChanged(
+ micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ inOrder.verify(mCallback).onActiveStateChanged(
+ micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
+ }
+
private class TestHandler extends AppOpsControllerImpl.H {
TestHandler(Looper looper) {
mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index bf2020b..48e5131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -517,7 +517,7 @@
assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality);
assertThat(mMessageCaptor.getValue()).isEqualTo(
- mContext.getString(R.string.biometric_face_not_recognized));
+ mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 7e6b74a..4d19543 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -527,6 +527,7 @@
val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
val size by collectLastValue(viewModel.size)
val legacyState by collectLastValue(viewModel.legacyState)
+ val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)
if (testCase.isCoex && testCase.authenticatedByFingerprint) {
viewModel.ensureFingerprintHasStarted(isDelayed = true)
@@ -535,7 +536,11 @@
viewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
+ if (confirmationRequired == true) {
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
+ } else {
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED)
+ }
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
assertThat(messageVisible).isTrue()
assertThat(authenticating).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index d6cafcb..5a5c058 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -211,7 +211,7 @@
context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y)
val expectedCenterX: Float
val expectedCenterY: Float
- when (context.display.rotation) {
+ when (checkNotNull(context.display).rotation) {
Surface.ROTATION_90 -> {
expectedCenterX = width * normalizedPortPosY
expectedCenterY = height * (1 - normalizedPortPosX)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
new file mode 100644
index 0000000..3df9cbb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -0,0 +1,285 @@
+package com.android.systemui.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.BroadcastReceiver
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+ @Mock private lateinit var appWidgetManager: AppWidgetManager
+
+ @Mock private lateinit var appWidgetHost: AppWidgetHost
+
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock private lateinit var packageManager: PackageManager
+
+ @Mock private lateinit var userManager: UserManager
+
+ @Mock private lateinit var userHandle: UserHandle
+
+ @Mock private lateinit var userTracker: UserTracker
+
+ @Mock private lateinit var featureFlags: FeatureFlags
+
+ @Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
+
+ private lateinit var logBuffer: LogBuffer
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ logBuffer = FakeLogBuffer.Factory.create()
+
+ featureFlagEnabled(true)
+ whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
+ whenever(userTracker.userHandle).thenReturn(userHandle)
+ }
+
+ @Test
+ fun broadcastReceiver_featureDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
+ testScope.runTest {
+ featureFlagEnabled(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+ verifyBroadcastReceiverNeverRegistered()
+ }
+
+ @Test
+ fun broadcastReceiver_featureEnabledAndUserUnlocked_doNotRegisterBroadcastReceiver() =
+ testScope.runTest {
+ userUnlocked(true)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+ verifyBroadcastReceiverNeverRegistered()
+ }
+
+ @Test
+ fun broadcastReceiver_featureEnabledAndUserLocked_registerBroadcastReceiver() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+ verifyBroadcastReceiverRegistered()
+ }
+
+ @Test
+ fun broadcastReceiver_whenFlowFinishes_unregisterBroadcastReceiver() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+
+ val job = launch { repository.stopwatchAppWidgetInfo.collect() }
+ runCurrent()
+ val receiver = broadcastReceiverUpdate()
+
+ job.cancel()
+ runCurrent()
+
+ Mockito.verify(broadcastDispatcher).unregisterReceiver(receiver)
+ }
+
+ @Test
+ fun stopwatch_whenUserUnlocks_receiveProviderInfo() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+ assertThat(lastStopwatchProviderInfo()).isNull()
+
+ userUnlocked(true)
+ installedProviders(listOf(stopwatchProviderInfo))
+ broadcastReceiverUpdate()
+
+ assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo)
+ }
+
+ @Test
+ fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() =
+ testScope.runTest {
+ userUnlocked(true)
+ installedProviders(listOf())
+
+ val repository = initCommunalWidgetRepository()
+
+ val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+ assertThat(lastStopwatchProviderInfo()).isNull()
+ }
+
+ @Test
+ fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() =
+ testScope.runTest {
+ userUnlocked(true)
+ installedProviders(listOf(stopwatchProviderInfo))
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+ Mockito.verify(appWidgetHost).allocateAppWidgetId()
+ }
+
+ @Test
+ fun appWidgetId_userLockedAgainAfterProviderInfoAvailable_deleteAppWidgetId() =
+ testScope.runTest {
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(123456)
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+ assertThat(lastStopwatchProviderInfo()).isNull()
+
+ // User unlocks
+ userUnlocked(true)
+ installedProviders(listOf(stopwatchProviderInfo))
+ broadcastReceiverUpdate()
+
+ // Verify app widget id allocated
+ assertThat(lastStopwatchProviderInfo()?.appWidgetId).isEqualTo(123456)
+ Mockito.verify(appWidgetHost).allocateAppWidgetId()
+ Mockito.verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt())
+
+ // User locked again
+ userUnlocked(false)
+ broadcastReceiverUpdate()
+
+ // Verify app widget id deleted
+ assertThat(lastStopwatchProviderInfo()).isNull()
+ Mockito.verify(appWidgetHost).deleteAppWidgetId(123456)
+ }
+
+ @Test
+ fun appWidgetHost_userUnlocked_startListening() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+ Mockito.verify(appWidgetHost, Mockito.never()).startListening()
+
+ userUnlocked(true)
+ broadcastReceiverUpdate()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+ Mockito.verify(appWidgetHost).startListening()
+ }
+
+ @Test
+ fun appWidgetHost_userLockedAgain_stopListening() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+ userUnlocked(true)
+ broadcastReceiverUpdate()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+ Mockito.verify(appWidgetHost).startListening()
+ Mockito.verify(appWidgetHost, Mockito.never()).stopListening()
+
+ userUnlocked(false)
+ broadcastReceiverUpdate()
+ collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+ Mockito.verify(appWidgetHost).stopListening()
+ }
+
+ private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
+ return CommunalWidgetRepositoryImpl(
+ appWidgetManager,
+ appWidgetHost,
+ broadcastDispatcher,
+ packageManager,
+ userManager,
+ userTracker,
+ logBuffer,
+ featureFlags,
+ )
+ }
+
+ private fun verifyBroadcastReceiverRegistered() {
+ Mockito.verify(broadcastDispatcher)
+ .registerReceiver(
+ any(),
+ any(),
+ nullable(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
+ }
+
+ private fun verifyBroadcastReceiverNeverRegistered() {
+ Mockito.verify(broadcastDispatcher, Mockito.never())
+ .registerReceiver(
+ any(),
+ any(),
+ nullable(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
+ }
+
+ private fun broadcastReceiverUpdate(): BroadcastReceiver {
+ val broadcastReceiverCaptor = kotlinArgumentCaptor<BroadcastReceiver>()
+ Mockito.verify(broadcastDispatcher)
+ .registerReceiver(
+ broadcastReceiverCaptor.capture(),
+ any(),
+ nullable(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
+ broadcastReceiverCaptor.value.onReceive(null, null)
+ return broadcastReceiverCaptor.value
+ }
+
+ private fun featureFlagEnabled(enabled: Boolean) {
+ whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
+ }
+
+ private fun userUnlocked(userUnlocked: Boolean) {
+ whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked)
+ }
+
+ private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
+ whenever(appWidgetManager.installedProviders).thenReturn(providers)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
new file mode 100644
index 0000000..d28f530
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+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.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RoboPilotTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalInteractorTest : SysuiTestCase() {
+ @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo
+
+ private lateinit var testScope: TestScope
+
+ private lateinit var widgetRepository: FakeCommunalWidgetRepository
+ private lateinit var interactor: CommunalInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+ widgetRepository = FakeCommunalWidgetRepository()
+ interactor = CommunalInteractor(widgetRepository)
+ }
+
+ @Test
+ fun testAppWidgetInfoFlow() =
+ testScope.runTest {
+ val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
+ runCurrent()
+ assertThat(lastAppWidgetInfo()).isNull()
+
+ widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo)
+ runCurrent()
+ assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
new file mode 100644
index 0000000..e3a75f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.communal.ui.view.layout.blueprints
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class DefaultCommunalBlueprintTest : SysuiTestCase() {
+ @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
+
+ private lateinit var blueprint: DefaultCommunalBlueprint
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ blueprint = DefaultCommunalBlueprint(widgetSection)
+ }
+
+ @Test
+ fun apply() {
+ val cs = ConstraintSet()
+ blueprint.apply(cs)
+ verify(widgetSection).apply(cs)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 42f28c8..2ae342a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -125,7 +125,7 @@
control
)
cvh.bindData(cws, false)
- val chevronIcon = baseLayout.findViewById<View>(R.id.chevron_icon)
+ val chevronIcon = baseLayout.requireViewById<View>(R.id.chevron_icon)
assertThat(chevronIcon.visibility).isEqualTo(View.VISIBLE)
}
@@ -138,4 +138,4 @@
private val DRAWABLE = GradientDrawable()
private val COLOR = ColorStateList.valueOf(0xffff00)
private val DEFAULT_CONTROL = Control.StatelessBuilder(
- CONTROL_ID, mock(PendingIntent::class.java)).build()
\ No newline at end of file
+ CONTROL_ID, mock(PendingIntent::class.java)).build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index fcd6568..a400ff9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -365,7 +365,8 @@
val selectedItems =
listOf(
SelectedItem.StructureItem(
- StructureInfo(ComponentName.unflattenFromString("pkg/.cls1"), "a", ArrayList())
+ StructureInfo(checkNotNull(ComponentName.unflattenFromString("pkg/.cls1")),
+ "a", ArrayList())
),
)
preferredPanelRepository.setSelectedComponent(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 04ebbf6..fe5b812 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -337,9 +337,6 @@
)
.isFalse()
- whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
- assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
-
whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index ae0a334..ef51e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -133,7 +133,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- context.resources.configuration.locales = LocaleList(Locale.US, Locale.UK)
+ context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK))
transitionRepository = FakeKeyguardTransitionRepository()
mediaCarouselController =
MediaCarouselController(
@@ -735,13 +735,13 @@
@Test
fun testOnLocaleListChanged_playersAreAddedBack() {
- context.resources.configuration.locales = LocaleList(Locale.US, Locale.UK, Locale.CANADA)
+ context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK, Locale.CANADA))
testConfigurationChange(configListener.value::onLocaleListChanged)
verify(pageIndicator, never()).tintList =
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
- context.resources.configuration.locales = LocaleList(Locale.UK, Locale.US, Locale.CANADA)
+ context.resources.configuration.setLocales(LocaleList(Locale.UK, Locale.US, Locale.CANADA))
testConfigurationChange(configListener.value::onLocaleListChanged)
verify(pageIndicator).tintList =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index ee3b80a..906420d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -123,7 +123,7 @@
private fun givenDisplay(width: Int, height: Int, isTablet: Boolean = false) {
val bounds = Rect(0, 0, width, height)
- val windowMetrics = WindowMetrics(bounds, null)
+ val windowMetrics = WindowMetrics(bounds, { null }, 1.0f)
whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics)
whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
index 3a74c72..7bd97ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -108,20 +108,6 @@
}
@Test
- fun mediaProjectionState_onSessionSet_tokenNull_emitsEntireScreen() =
- testScope.runTest {
- val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
-
- fakeMediaProjectionManager.dispatchOnSessionSet(
- session =
- ContentRecordingSession.createTaskSession(/* taskWindowContainerToken= */ null)
- )
-
- assertThat(state).isEqualTo(MediaProjectionState.EntireScreen)
- }
-
- @Test
fun mediaProjectionState_sessionSet_taskWithToken_noMatchingRunningTask_emitsEntireScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index fab1de0..2d3dc58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -73,7 +73,7 @@
context,
windowManager,
ViewConfiguration.get(context),
- Handler.createAsync(Looper.myLooper()),
+ Handler.createAsync(checkNotNull(Looper.myLooper())),
vibratorHelper,
configurationController,
latencyTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index d933b57..1536c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -69,6 +69,7 @@
import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import kotlin.test.assertNotNull
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -672,7 +673,7 @@
extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
iconCaptor.value?.let { icon ->
- assertThat(icon).isNotNull()
+ assertNotNull(icon)
assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
}
}
@@ -755,7 +756,7 @@
assertThat(shortLabel).isEqualTo(NOTE_TASK_SHORT_LABEL)
assertThat(longLabel).isEqualTo(NOTE_TASK_LONG_LABEL)
assertThat(isLongLived).isEqualTo(true)
- assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
+ assertThat(icon?.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
assertThat(extras?.getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE))
.isEqualTo(NOTE_TASK_PACKAGE_NAME)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
index db96d55..14ecf93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
@@ -143,6 +143,16 @@
}
@Test
+ fun testVoiceActivationPrivacyItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, TEST_UID,
+ TEST_PACKAGE_NAME, 0)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+ assertEquals(1, privacyItems.size)
+ assertEquals(PrivacyType.TYPE_MICROPHONE, privacyItems[0].privacyType)
+ }
+
+ @Test
fun testSimilarItemsDifferentTimeStamp() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 1)))
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 fda63ed..72c31b1 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
@@ -32,6 +32,7 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -372,12 +373,23 @@
assertThat(loadTilesForUser(0)).isNull()
}
+ @Test
+ fun emptyTilesReplacedByDefaultInSettings() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tilesSpecs(0))
+ runCurrent()
+
+ assertThat(loadTilesForUser(0))
+ .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
+ }
+
private fun getDefaultTileSpecs(): List<TileSpec> {
return QSHost.getDefaultSpecs(context.resources).map(TileSpec::create)
}
- private fun storeTilesForUser(specs: String, forUser: Int) {
+ private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
+ runCurrent()
}
private fun loadTilesForUser(forUser: Int): String? {
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 30cea2d..6689514 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
@@ -648,6 +648,22 @@
assertThat(tiles!![1].spec).isEqualTo(CUSTOM_TILE_SPEC)
}
+ @Test
+ fun tileAddedOnEmptyList_blocked() =
+ testScope.runTest(USER_INFO_0) {
+ val tiles by collectLastValue(underTest.currentTiles)
+ val specs = listOf(TileSpec.create("a"), TileSpec.create("b"))
+ val newTile = TileSpec.create("c")
+
+ underTest.addTile(newTile)
+
+ assertThat(tiles!!.isEmpty()).isTrue()
+
+ tileSpecRepository.setTiles(USER_INFO_0.id, specs)
+
+ assertThat(tiles!!.size).isEqualTo(3)
+ }
+
private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
this.state = state
this.label = label
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 07feedf..ad6909d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -126,6 +126,7 @@
private fun onSpinnerItemSelected(position: Int) {
val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
- spinner.onItemSelectedListener.onItemSelected(spinner, mock(), position, /* id= */ 0)
+ checkNotNull(spinner.onItemSelectedListener)
+ .onItemSelected(spinner, mock(), position, /* id= */ 0)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 112a09b..577b6e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -584,7 +584,7 @@
private fun emptyInsets() = mock(WindowInsets::class.java)
private fun WindowInsets.withCutout(): WindowInsets {
- whenever(displayCutout.safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
+ whenever(checkNotNull(displayCutout).safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
return this
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 8d3c4b2..405199e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -567,7 +567,7 @@
private fun emptyInsets() = mock(WindowInsets::class.java)
private fun WindowInsets.withCutout(): WindowInsets {
- whenever(displayCutout.safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
+ whenever(checkNotNull(displayCutout).safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
return this
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index 58b44ae..19dc72d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -236,7 +236,8 @@
`when`(precondition.conditionsMet()).thenReturn(true)
// Given a session is created
- val weatherView = controller.buildAndConnectWeatherView(fakeParent, customView)
+ val weatherView =
+ checkNotNull(controller.buildAndConnectWeatherView(fakeParent, customView))
controller.stateChangeListener.onViewAttachedToWindow(weatherView)
verify(smartspaceManager).createSmartspaceSession(any())
@@ -258,7 +259,8 @@
// Given a session is created
val customView = Mockito.mock(TestView::class.java)
- val weatherView = controller.buildAndConnectWeatherView(fakeParent, customView)
+ val weatherView =
+ checkNotNull(controller.buildAndConnectWeatherView(fakeParent, customView))
controller.stateChangeListener.onViewAttachedToWindow(weatherView)
verify(smartspaceManager).createSmartspaceSession(any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
index 724ea02..e4da53a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
@@ -47,7 +47,7 @@
processor = MediaArtworkProcessor()
val point = Point()
- context.display.getSize(point)
+ checkNotNull(context.display).getSize(point)
screenWidth = point.x
screenHeight = point.y
}
@@ -106,4 +106,4 @@
// THEN the processed bitmap is null
assertThat(background).isNull()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 2de5705..9036f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -278,7 +278,7 @@
`when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
// WHEN a connection attempt is made and view is attached
- val view = controller.buildAndConnectView(fakeParent)
+ val view = controller.buildAndConnectView(fakeParent)!!
controller.stateChangeListener.onViewAttachedToWindow(view)
// THEN no session is created
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
index 03d3854..56d2397 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
@@ -212,13 +212,13 @@
@Test
fun localeListChanged_listenerNotified() {
val config = mContext.resources.configuration
- config.locales = LocaleList(Locale.CANADA, Locale.GERMANY)
+ config.setLocales(LocaleList(Locale.CANADA, Locale.GERMANY))
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the locales are updated
- config.locales = LocaleList(Locale.FRANCE, Locale.JAPAN, Locale.CHINESE)
+ config.setLocales(LocaleList(Locale.FRANCE, Locale.JAPAN, Locale.CHINESE))
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 1759fb7..210c5ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -463,10 +463,10 @@
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
- configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
+ configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
- configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
+ configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 800, 600))
// WHEN: get insets on the second display
val secondDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -482,14 +482,14 @@
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
- configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
+ configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
val firstDisplayInsetsFirstCall = provider
.getStatusBarContentAreaForRotation(ROTATION_NONE)
- configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
+ configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 800, 600))
provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
- configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
+ configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
// WHEN: get insets on the first display again
val firstDisplayInsetsSecondCall = provider
@@ -577,4 +577,4 @@
" expected=$expected actual=$actual",
expected.equals(actual))
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
index fc5f782..1125d41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
@@ -109,6 +109,7 @@
WallpaperLocalColorExtractor colorExtractor = new WallpaperLocalColorExtractor(
mBackgroundExecutor,
+ new Object(),
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
@Override
public void onColorsProcessed(List<RectF> regions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
new file mode 100644
index 0000000..1a8c583
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -0,0 +1,15 @@
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [CommunalWidgetRepository] */
+class FakeCommunalWidgetRepository : CommunalWidgetRepository {
+ private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
+ override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
+
+ fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
+ _stopwatchAppWidgetInfo.value = appWidgetInfo
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 9b9593b..8060d5a 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -637,7 +637,7 @@
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
switch (event.getActionMasked()) {
case ACTION_DOWN:
- // We should have already received ACTION_DOWN. Ignore.
+ handleActionDownStateTouchExploring(event, rawEvent, policyFlags);
break;
case ACTION_POINTER_DOWN:
handleActionPointerDown(event, rawEvent, policyFlags);
@@ -843,6 +843,15 @@
}
}
+ private void handleActionDownStateTouchExploring(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ // This is an interrupted and continued touch exploration. Maintain the consistency of the
+ // event stream.
+ mSendTouchExplorationEndDelayed.cancel();
+ mSendTouchInteractionEndDelayed.cancel();
+ sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
+ }
+
/**
* Handles move events while touch exploring. this is also where we drag or delegate based on
* the number of fingers moving on the screen.
@@ -1100,12 +1109,15 @@
}
/**
- * Sends the enter events if needed. Such events are hover enter and touch explore
- * gesture start.
+ * Sends the enter events if needed. Such events are hover enter and touch explore gesture
+ * start.
*
* @param policyFlags The policy flags associated with the event.
*/
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
+ if (!mState.isTouchExploring()) {
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ }
MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
@@ -1118,7 +1130,6 @@
}
}
-
/**
* Determines whether a two pointer gesture is a dragging one.
*
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9f263c8..a4d72b1 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,7 +98,10 @@
java_library_static {
name: "services.core.unboosted",
- defaults: ["platform_service_defaults"],
+ defaults: [
+ "platform_service_defaults",
+ "android.hardware.power-java_static",
+ ],
srcs: [
":android.hardware.biometrics.face-V3-java-source",
":android.hardware.tv.hdmi.connection-V1-java-source",
@@ -152,7 +155,7 @@
"android.hardware.boot-V1.0-java", // HIDL
"android.hardware.boot-V1.1-java", // HIDL
"android.hardware.boot-V1.2-java", // HIDL
- "android.hardware.boot-V1-java", // AIDL
+ "android.hardware.boot-V1-java", // AIDL
"android.hardware.broadcastradio-V2.0-java", // HIDL
"android.hardware.broadcastradio-V1-java", // AIDL
"android.hardware.health-V1.0-java", // HIDL
@@ -176,7 +179,6 @@
"android.hardware.ir-V1-java",
"android.hardware.rebootescrow-V1-java",
"android.hardware.power.stats-V2-java",
- "android.hardware.power-V4-java",
"android.hidl.manager-V1.2-java",
"cbor-java",
"icu4j_calendar_astronomer",
@@ -188,6 +190,7 @@
"securebox",
"apache-commons-math",
"power_optimization_flags_lib",
+ "notification_flags_lib",
],
javac_shard_size: 50,
javacflags: [
@@ -237,4 +240,4 @@
prebuilt_etc {
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a53c2fb..4dc76db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7409,6 +7409,9 @@
case BugreportParams.BUGREPORT_MODE_WIFI:
type = "bugreportwifi";
break;
+ case BugreportParams.BUGREPORT_MODE_ONBOARDING:
+ type = "bugreportonboarding";
+ break;
default:
throw new IllegalArgumentException(
"Provided bugreport type is not correct, value: "
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index f7bbc8b..8a8e2af 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -286,6 +286,12 @@
+ ")");
}
+ private String getFgsInfoForWtf() {
+ return " cmp: " + this.getComponentName().toShortString()
+ + " sdk: " + this.appInfo.targetSdkVersion
+ ;
+ }
+
void maybeLogFgsLogicChange() {
final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
mAllowWIUInBindService);
@@ -311,7 +317,8 @@
+ " OS:" // Orig-start
+ changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService)
+ " NS:" // New-start
- + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings);
+ + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings)
+ + getFgsInfoForWtf();
Slog.wtf(TAG_SERVICE, message);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index cb5e7f1..2ae3118 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -150,6 +150,10 @@
// Timestamp when hardware authentication occurred
private long mAuthenticatedTimeMs;
+ @NonNull
+ private final OperationContextExt mOperationContext;
+
+
AuthSession(@NonNull Context context,
@NonNull BiometricContext biometricContext,
@NonNull IStatusBarService statusBarService,
@@ -215,6 +219,7 @@
mFingerprintSensorProperties = fingerprintSensorProperties;
mCancelled = false;
mBiometricFrameworkStatsLogger = logger;
+ mOperationContext = new OperationContextExt(true /* isBP */);
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -581,6 +586,8 @@
} else {
Slog.d(TAG, "delaying fingerprint sensor start");
}
+
+ mBiometricContext.updateContext(mOperationContext, isCrypto());
}
// call once anytime after onDialogAnimatedIn() to indicate it's appropriate to start the
@@ -743,12 +750,12 @@
+ ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+ ", RequireConfirmation: " + mPreAuthInfo.confirmationRequested
+ ", State: " + FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
- + ", Latency: " + latency);
+ + ", Latency: " + latency
+ + ", SessionId: " + mOperationContext.getId());
}
mBiometricFrameworkStatsLogger.authenticate(
- mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
- isCrypto()),
+ mOperationContext,
statsModality(),
BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
@@ -780,13 +787,13 @@
+ ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+ ", Reason: " + reason
+ ", Error: " + error
- + ", Latency: " + latency);
+ + ", Latency: " + latency
+ + ", SessionId: " + mOperationContext.getId());
}
// Auth canceled
if (error != 0) {
mBiometricFrameworkStatsLogger.error(
- mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
- isCrypto()),
+ mOperationContext,
statsModality(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStats.java b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
new file mode 100644
index 0000000..137a418
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
@@ -0,0 +1,90 @@
+/*
+ * 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.biometrics;
+
+/**
+ * Utility class for on-device biometric authentication data, including total authentication,
+ * rejections, and the number of sent enrollment notifications.
+ */
+public class AuthenticationStats {
+
+ private final int mUserId;
+ private int mTotalAttempts;
+ private int mRejectedAttempts;
+ private int mEnrollmentNotifications;
+ private final int mModality;
+
+ public AuthenticationStats(final int userId, int totalAttempts, int rejectedAttempts,
+ int enrollmentNotifications, final int modality) {
+ mUserId = userId;
+ mTotalAttempts = totalAttempts;
+ mRejectedAttempts = rejectedAttempts;
+ mEnrollmentNotifications = enrollmentNotifications;
+ mModality = modality;
+ }
+
+ public AuthenticationStats(final int userId, final int modality) {
+ mUserId = userId;
+ mTotalAttempts = 0;
+ mRejectedAttempts = 0;
+ mEnrollmentNotifications = 0;
+ mModality = modality;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public int getTotalAttempts() {
+ return mTotalAttempts;
+ }
+
+ public int getRejectedAttempts() {
+ return mRejectedAttempts;
+ }
+
+ public int getEnrollmentNotifications() {
+ return mEnrollmentNotifications;
+ }
+
+ public int getModality() {
+ return mModality;
+ }
+
+ /** Calculate FRR. */
+ public float getFrr() {
+ if (mTotalAttempts > 0) {
+ return mRejectedAttempts / (float) mTotalAttempts;
+ } else {
+ return -1.0f;
+ }
+ }
+
+ /** Update total authentication attempts and rejections. */
+ public void authenticate(boolean authenticated) {
+ if (!authenticated) {
+ mRejectedAttempts++;
+ }
+ mTotalAttempts++;
+ }
+
+ /** Reset total authentication attempts and rejections. */
+ public void resetData() {
+ mTotalAttempts = 0;
+ mRejectedAttempts = 0;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
new file mode 100644
index 0000000..c9cd814
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -0,0 +1,106 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Calculate and collect on-device False Rejection Rates (FRR).
+ * FRR = All [given biometric modality] unlock failures / all [given biometric modality] unlock
+ * attempts.
+ */
+public class AuthenticationStatsCollector {
+
+ private static final String TAG = "AuthenticationStatsCollector";
+
+ // The minimum number of attempts that will calculate the FRR and trigger the notification.
+ private static final int MINIMUM_ATTEMPTS = 500;
+ // The maximum number of eligible biometric enrollment notification can be sent.
+ private static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
+
+ private final float mThreshold;
+ private final int mModality;
+
+ @NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
+
+ public AuthenticationStatsCollector(@NonNull Context context, int modality) {
+ mThreshold = context.getResources()
+ .getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1);
+ mUserAuthenticationStatsMap = new HashMap<>();
+ mModality = modality;
+ }
+
+ /** Update total authentication and rejected attempts. */
+ public void authenticate(int userId, boolean authenticated) {
+ // Check if this is a new user.
+ if (!mUserAuthenticationStatsMap.containsKey(userId)) {
+ mUserAuthenticationStatsMap.put(userId, new AuthenticationStats(userId, mModality));
+ }
+
+ AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
+
+ authenticationStats.authenticate(authenticated);
+
+ persistDataIfNeeded(userId);
+ sendNotificationIfNeeded(userId);
+ }
+
+ private void sendNotificationIfNeeded(int userId) {
+ AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
+ if (authenticationStats.getTotalAttempts() >= MINIMUM_ATTEMPTS) {
+ // Send notification if FRR exceeds the threshold
+ if (authenticationStats.getEnrollmentNotifications() < MAXIMUM_ENROLLMENT_NOTIFICATIONS
+ && authenticationStats.getFrr() >= mThreshold) {
+ // TODO(wenhuiy): Send notifications.
+ }
+
+ authenticationStats.resetData();
+ }
+ }
+
+ private void persistDataIfNeeded(int userId) {
+ AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
+ if (authenticationStats.getTotalAttempts() % 50 == 0) {
+ // TODO(wenhuiy): Persist data.
+ }
+ }
+
+ /**
+ * Only being used in tests. Callers should not make any changes to the returned
+ * authentication stats.
+ *
+ * @return AuthenticationStats of the user, or null if the stats doesn't exist.
+ */
+ @Nullable
+ @VisibleForTesting
+ AuthenticationStats getAuthenticationStatsForUser(int userId) {
+ return mUserAuthenticationStatsMap.getOrDefault(userId, null);
+ }
+
+ @VisibleForTesting
+ void setAuthenticationStatsForUser(int userId, AuthenticationStats authenticationStats) {
+ mUserAuthenticationStatsMap.put(userId, authenticationStats);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index c76a2e3..87037af 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -27,6 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
/**
@@ -41,6 +42,7 @@
private final int mStatsAction;
private final int mStatsClient;
private final BiometricFrameworkStatsLogger mSink;
+ @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
@NonNull private final ALSProbe mALSProbe;
private long mFirstAcquireTimeMs;
@@ -49,7 +51,8 @@
/** Get a new logger with all unknown fields (for operations that do not require logs). */
public static BiometricLogger ofUnknown(@NonNull Context context) {
return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
- BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
+ null /* AuthenticationStatsCollector */);
}
/**
@@ -64,26 +67,32 @@
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
*/
public BiometricLogger(
- @NonNull Context context, int statsModality, int statsAction, int statsClient) {
+ @NonNull Context context, int statsModality, int statsAction, int statsClient,
+ AuthenticationStatsCollector authenticationStatsCollector) {
this(statsModality, statsAction, statsClient,
BiometricFrameworkStatsLogger.getInstance(),
+ authenticationStatsCollector,
context.getSystemService(SensorManager.class));
}
@VisibleForTesting
BiometricLogger(
int statsModality, int statsAction, int statsClient,
- BiometricFrameworkStatsLogger logSink, SensorManager sensorManager) {
+ BiometricFrameworkStatsLogger logSink,
+ @NonNull AuthenticationStatsCollector statsCollector,
+ SensorManager sensorManager) {
mStatsModality = statsModality;
mStatsAction = statsAction;
mStatsClient = statsClient;
mSink = logSink;
+ mAuthenticationStatsCollector = statsCollector;
mALSProbe = new ALSProbe(sensorManager);
}
/** Creates a new logger with the action replaced with the new action. */
public BiometricLogger swapAction(@NonNull Context context, int statsAction) {
- return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient);
+ return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient,
+ null /* AuthenticationStatsCollector */);
}
/** Disable logging metrics and only log critical events, such as system health issues. */
@@ -192,10 +201,13 @@
public void logOnAuthenticated(Context context, OperationContextExt operationContext,
boolean authenticated, boolean requireConfirmation, int targetUserId,
boolean isBiometricPrompt) {
+ // Do not log metrics when fingerprint enrollment reason is ENROLL_FIND_SENSOR
if (!mShouldLogMetrics) {
return;
}
+ mAuthenticationStatsCollector.authenticate(targetUserId, authenticated);
+
int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
if (!authenticated) {
authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index 4a10e8e..f78ca43 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -103,8 +103,14 @@
*/
@NonNull
public OperationContext toAidlContext(@NonNull FingerprintAuthenticateOptions options) {
- mAidlContext.authenticateReason = AuthenticateReason
- .fingerprintAuthenticateReason(getAuthReason(options));
+ if (options.getVendorReason() != null) {
+ mAidlContext.authenticateReason = AuthenticateReason
+ .vendorAuthenticateReason(options.getVendorReason());
+
+ } else {
+ mAidlContext.authenticateReason = AuthenticateReason
+ .fingerprintAuthenticateReason(getAuthReason(options));
+ }
mAidlContext.wakeReason = getWakeReason(options);
return mAidlContext;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 33ed63c..a7d160c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -49,6 +49,7 @@
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -112,6 +113,8 @@
private final BiometricContext mBiometricContext;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final AuthenticationStatsCollector mAuthenticationStatsCollector;
@Nullable
private IFace mDaemon;
@@ -173,6 +176,9 @@
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
+ BiometricsProtoEnums.MODALITY_FACE);
+
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -283,7 +289,8 @@
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds());
@@ -341,7 +348,8 @@
final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
@@ -372,7 +380,8 @@
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext);
scheduleForSensor(sensorId, client);
});
@@ -386,7 +395,8 @@
mFaceSensors.get(sensorId).getLazySession(), token, userId,
opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
@@ -407,7 +417,8 @@
opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent);
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
mBiometricStateCallback, new ClientMonitorCallback() {
@@ -443,7 +454,8 @@
final FaceDetectClient client = new FaceDetectClient(mContext,
mFaceSensors.get(sensorId).getLazySession(),
token, id, callback, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
@@ -471,7 +483,8 @@
mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mUsageStats, mFaceSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
@@ -540,7 +553,8 @@
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -554,7 +568,8 @@
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
mFaceSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
@@ -624,7 +639,8 @@
mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
FaceUtils.getInstance(sensorId),
mFaceSensors.get(sensorId).getAuthenticatorIds());
@@ -636,9 +652,10 @@
});
}
- private BiometricLogger createLogger(int statsAction, int statsClient) {
+ private BiometricLogger createLogger(int statsAction, int statsClient,
+ AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
- statsAction, statsClient);
+ statsAction, statsClient, authenticationStatsCollector);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 1e33c96..10991d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -52,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -124,6 +125,7 @@
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
@NonNull private final BiometricContext mBiometricContext;
+ @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -364,6 +366,9 @@
mCurrentUserId = UserHandle.USER_NULL;
});
+ mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
+ BiometricsProtoEnums.MODALITY_FACE);
+
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
@@ -554,7 +559,7 @@
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, sSystemClock.millis());
mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -586,7 +591,7 @@
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, userId, opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -617,7 +622,7 @@
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -677,7 +682,8 @@
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
mLazyDaemon, token, requestId, receiver, operationId, restricted,
options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric, mLockoutTracker,
mUsageStats, allowBackgroundAuthentication,
Utils.getCurrentStrength(mSensorId));
@@ -713,7 +719,7 @@
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -731,7 +737,7 @@
opPackageName,
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -750,7 +756,7 @@
final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken);
mScheduler.scheduleClientMonitor(client);
});
@@ -821,7 +827,7 @@
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback,
@@ -953,7 +959,7 @@
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -968,9 +974,10 @@
});
}
- private BiometricLogger createLogger(int statsAction, int statsClient) {
+ private BiometricLogger createLogger(int statsAction, int statsClient,
+ AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
- statsAction, statsClient);
+ statsAction, statsClient, authenticationStatsCollector);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 0421d78..2d062db 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -57,6 +57,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -122,6 +123,7 @@
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
private AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -181,6 +183,9 @@
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
for (SensorProps prop : props) {
@@ -338,7 +343,8 @@
mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
@@ -363,7 +369,8 @@
mContext, mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
mFingerprintSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
@@ -380,7 +387,7 @@
mFingerprintSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
scheduleForSensor(sensorId, client);
});
@@ -395,7 +402,8 @@
mFingerprintSensors.get(sensorId).getLazySession(), token,
userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
@@ -415,7 +423,7 @@
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
@@ -455,7 +463,8 @@
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token, id, callback,
options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
@@ -477,7 +486,8 @@
mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mFingerprintSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController,
@@ -566,7 +576,8 @@
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -588,7 +599,8 @@
mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
FingerprintUtils.getInstance(sensorId),
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
@@ -600,9 +612,10 @@
});
}
- private BiometricLogger createLogger(int statsAction, int statsClient) {
+ private BiometricLogger createLogger(int statsAction, int statsClient,
+ AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- statsAction, statsClient);
+ statsAction, statsClient, authenticationStatsCollector);
}
@Override
@@ -635,7 +648,8 @@
new FingerprintInvalidationClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 92b216d..4b07dca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -52,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -123,6 +124,7 @@
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
@NonNull private final BiometricContext mBiometricContext;
+ @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -351,6 +353,9 @@
mCurrentUserId = UserHandle.USER_NULL;
});
+ mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
@@ -497,7 +502,8 @@
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -544,7 +550,8 @@
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
userId, mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
@@ -559,7 +566,8 @@
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
@@ -573,7 +581,8 @@
mContext, mLazyDaemon, token, userId, opPackageName,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
@@ -594,7 +603,7 @@
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -639,7 +648,8 @@
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mLazyDaemon, token, id, listener, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -660,7 +670,8 @@
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, requestId, listener, operationId,
restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
@@ -706,7 +717,7 @@
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -726,7 +737,7 @@
FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -741,7 +752,7 @@
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN),
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
@@ -762,9 +773,10 @@
mBiometricStateCallback));
}
- private BiometricLogger createLogger(int statsAction, int statsClient) {
+ private BiometricLogger createLogger(int statsAction, int statsClient,
+ AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- statsAction, statsClient);
+ statsAction, statsClient, authenticationStatsCollector);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 25e704b..0f89a6e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -254,13 +254,6 @@
// The doze screen brightness.
private final float mScreenBrightnessDozeConfig;
- // The dim screen brightness.
- private final float mScreenBrightnessDimConfig;
-
- // The minimum dim amount to use if the screen brightness is already below
- // mScreenBrightnessDimConfig.
- private final float mScreenBrightnessMinimumDimAmount;
-
// True if auto-brightness should be used.
private boolean mUseSoftwareAutoBrightnessConfig;
@@ -529,11 +522,6 @@
// DOZE AND DIM SETTINGS
mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
- mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness(
- pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
- mScreenBrightnessMinimumDimAmount = resources.getFloat(
- R.dimen.config_screenBrightnessMinimumDimAmountFloat);
-
loadBrightnessRampRates();
mSkipScreenOnBrightnessRamp = resources.getBoolean(
R.bool.config_skipScreenOnBrightnessRamp);
@@ -565,7 +553,7 @@
mUniqueDisplayId,
mThermalBrightnessThrottlingDataId,
mDisplayDeviceConfig
- ));
+ ), mContext);
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -1426,6 +1414,7 @@
// Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
// we broadcast this change through setting.
final float unthrottledBrightnessState = brightnessState;
+
if (mBrightnessThrottler.isThrottled()) {
mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap());
brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
@@ -1449,25 +1438,6 @@
mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessState);
}
- // Apply dimming by at least some minimum amount when user activity
- // timeout is about to expire.
- if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
- if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
- brightnessState = Math.max(
- Math.min(brightnessState - mScreenBrightnessMinimumDimAmount,
- mScreenBrightnessDimConfig),
- PowerManager.BRIGHTNESS_MIN);
- mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
- }
- if (!mAppliedDimming) {
- slowChange = false;
- }
- mAppliedDimming = true;
- } else if (mAppliedDimming) {
- slowChange = false;
- mAppliedDimming = false;
- }
-
DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
brightnessState, slowChange);
@@ -2366,7 +2336,6 @@
pw.println();
pw.println("Display Power Controller Configuration:");
pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
- pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index f19d00b..9b28989 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -61,13 +62,13 @@
private Type mClamperType = null;
public BrightnessClamperController(Handler handler,
- ClamperChangeListener clamperChangeListener, DisplayDeviceData data) {
- this(new Injector(), handler, clamperChangeListener, data);
+ ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) {
+ this(new Injector(), handler, clamperChangeListener, data, context);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
- ClamperChangeListener clamperChangeListener, DisplayDeviceData data) {
+ ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) {
mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler = handler;
mClamperChangeListenerExternal = clamperChangeListener;
@@ -85,6 +86,7 @@
mClampers.add(
new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data));
}
+ mModifiers.add(new DisplayDimModifier(context));
mModifiers.add(new BrightnessLowPowerModeModifier());
start();
}
@@ -111,6 +113,7 @@
for (int i = 0; i < mModifiers.size(); i++) {
mModifiers.get(i).apply(request, builder);
}
+
return builder.build();
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
index f48ad2f..b478952 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
@@ -18,44 +18,37 @@
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
-import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import java.io.PrintWriter;
-class BrightnessLowPowerModeModifier implements BrightnessModifier {
-
- private boolean mAppliedLowPower = false;
+class BrightnessLowPowerModeModifier extends BrightnessModifier {
@Override
- public void apply(DisplayManagerInternal.DisplayPowerRequest request,
- DisplayBrightnessState.Builder stateBuilder) {
- // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
- // as long as it is above the minimum threshold.
- if (request.lowPowerMode) {
- float value = stateBuilder.getBrightness();
- if (value > PowerManager.BRIGHTNESS_MIN) {
- final float brightnessFactor =
- Math.min(request.screenLowPowerBrightnessFactor, 1);
- final float lowPowerBrightnessFloat = Math.max((value * brightnessFactor),
- PowerManager.BRIGHTNESS_MIN);
- stateBuilder.setBrightness(lowPowerBrightnessFloat);
- stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_LOW_POWER);
- }
- if (!mAppliedLowPower) {
- stateBuilder.setIsSlowChange(false);
- }
- mAppliedLowPower = true;
- } else if (mAppliedLowPower) {
- stateBuilder.setIsSlowChange(false);
- mAppliedLowPower = false;
- }
+ boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request) {
+ return request.lowPowerMode;
+ }
+
+
+ @Override
+ float getBrightnessAdjusted(float currentBrightness,
+ DisplayManagerInternal.DisplayPowerRequest request) {
+ final float brightnessFactor =
+ Math.min(request.screenLowPowerBrightnessFactor, 1);
+ return Math.max((currentBrightness * brightnessFactor), PowerManager.BRIGHTNESS_MIN);
+ }
+
+ @Override
+ int getModifier() {
+ return BrightnessReason.MODIFIER_LOW_POWER;
}
@Override
public void dump(PrintWriter pw) {
pw.println("BrightnessLowPowerModeModifier:");
- pw.println(" mAppliedLowPower=" + mAppliedLowPower);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ super.dump(ipw);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
index 3a33df6..112e63d 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness.clamper;
import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
@@ -25,10 +26,39 @@
/**
* Modifies current brightness based on request
*/
-interface BrightnessModifier {
+abstract class BrightnessModifier {
+
+ private boolean mApplied = false;
+
+ abstract boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request);
+
+ abstract float getBrightnessAdjusted(float currentBrightness,
+ DisplayManagerInternal.DisplayPowerRequest request);
+
+ abstract int getModifier();
void apply(DisplayManagerInternal.DisplayPowerRequest request,
- DisplayBrightnessState.Builder builder);
+ DisplayBrightnessState.Builder stateBuilder) {
+ // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
+ // as long as it is above the minimum threshold.
+ if (shouldApply(request)) {
+ float value = stateBuilder.getBrightness();
+ if (value > PowerManager.BRIGHTNESS_MIN) {
+ stateBuilder.setBrightness(getBrightnessAdjusted(value, request));
+ stateBuilder.getBrightnessReason().addModifier(getModifier());
+ }
+ if (!mApplied) {
+ stateBuilder.setIsSlowChange(false);
+ }
+ mApplied = true;
+ } else if (mApplied) {
+ stateBuilder.setIsSlowChange(false);
+ mApplied = false;
+ }
+ }
- void dump(PrintWriter pw);
+ void dump(PrintWriter pw) {
+ pw.println("BrightnessModifier:");
+ pw.println(" mApplied=" + mApplied);
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
new file mode 100644
index 0000000..4ff7bdb
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
@@ -0,0 +1,79 @@
+/*
+ * 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.display.brightness.clamper;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.R;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+class DisplayDimModifier extends BrightnessModifier {
+
+ // The dim screen brightness.
+ private final float mScreenBrightnessDimConfig;
+
+ // The minimum dim amount to use if the screen brightness is already below
+ // mScreenBrightnessDimConfig.
+ private final float mScreenBrightnessMinimumDimAmount;
+
+ DisplayDimModifier(Context context) {
+ PowerManager pm = Objects.requireNonNull(context.getSystemService(PowerManager.class));
+ Resources resources = context.getResources();
+
+ mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
+ mScreenBrightnessMinimumDimAmount = resources.getFloat(
+ R.dimen.config_screenBrightnessMinimumDimAmountFloat);
+ }
+
+
+ @Override
+ boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request) {
+ return request.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+ }
+
+ @Override
+ float getBrightnessAdjusted(float currentBrightness,
+ DisplayManagerInternal.DisplayPowerRequest request) {
+ return Math.max(
+ Math.min(currentBrightness - mScreenBrightnessMinimumDimAmount,
+ mScreenBrightnessDimConfig),
+ PowerManager.BRIGHTNESS_MIN);
+ }
+
+ @Override
+ int getModifier() {
+ return BrightnessReason.MODIFIER_DIMMED;
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("DisplayDimModifier:");
+ pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
+ pw.println(" mScreenBrightnessMinimumDimAmount=" + mScreenBrightnessMinimumDimAmount);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ super.dump(ipw);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 77213bf..b78f8a7 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -357,7 +357,7 @@
static final int INVALID_PORT_ID = HdmiDeviceInfo.PORT_INVALID;
static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID;
- static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL;
+ static final int TV_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INTERNAL;
// The relationship from one path (physical address) to another.
@IntDef({
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index dc416b2..824c8db 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -518,6 +518,18 @@
@ServiceThreadOnly
protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
assertRunOnServiceThread();
+ // If the device is active source and received a <Routing Change> or <Routing Information>
+ // message to a physical address in the same active path do not change the Active Source
+ // status.
+ // E.g. TV [0.0.0.0] ------ Switch [2.0.0.0] ------ OTT [2.1.0.0] (Active Source)
+ // TV sends <Routing Change>[2.0.0.0] -> OTT is still Active Source
+ // TV sends <Routing Change>[0.0.0.0] -> OTT is not Active Source anymore.
+ // TV sends <Routing Change>[3.0.0.0] -> OTT is not Active Source anymore.
+ if (HdmiUtils.isInActiveRoutingPath(mService.getPhysicalAddress(), physicalAddress)
+ && physicalAddress != Constants.TV_PHYSICAL_ADDRESS
+ && isActiveSource()) {
+ return;
+ }
if (physicalAddress != mService.getPhysicalAddress()) {
setActiveSource(physicalAddress,
"HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation()");
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e7a3db7..e96963b9 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -113,7 +113,7 @@
}
int param = tv().getActivePath();
return param != Constants.INVALID_PHYSICAL_ADDRESS
- ? param : Constants.PATH_INTERNAL;
+ ? param : Constants.TV_PHYSICAL_ADDRESS;
}
private void handleSendSystemAudioModeRequestTimeout() {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index e97a12a..6f9b7d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -295,6 +295,8 @@
private final PowerManager.WakeLock mDownloadPsdsWakeLock;
@GuardedBy("mLock")
private final Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>();
+ @GuardedBy("mLock")
+ private final Set<Integer> mDownloadInProgressPsdsTypes = new HashSet<>();
/**
* Properties loaded from PROPERTIES_FILE.
@@ -767,8 +769,16 @@
return;
}
synchronized (mLock) {
+ if (mDownloadInProgressPsdsTypes.contains(psdsType)) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "PSDS type " + psdsType + " download in progress. Ignore the request.");
+ }
+ return;
+ }
// hold wake lock while task runs
mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS);
+ mDownloadInProgressPsdsTypes.add(psdsType);
}
Log.i(TAG, "WakeLock acquired by handleDownloadPsdsData()");
Executors.newSingleThreadExecutor().execute(() -> {
@@ -818,6 +828,7 @@
Log.e(TAG, "WakeLock expired before release in "
+ "handleDownloadPsdsData()");
}
+ mDownloadInProgressPsdsTypes.remove(psdsType);
}
});
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 63dc59c..37bcfbb 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1291,13 +1291,18 @@
}
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
- if (managerRecord.mLastSessionCreationRequest != null) {
+ SessionCreationRequest lastRequest = managerRecord.mLastSessionCreationRequest;
+ if (lastRequest != null) {
+ Slog.i(
+ TAG,
+ TextUtils.formatSimple(
+ "requestCreateSessionWithManagerLocked: Notifying failure for pending"
+ + " session creation request - oldSession: %s, route: %s",
+ lastRequest.mOldSession, lastRequest.mRoute));
managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager(
managerRecord.mManager,
- toOriginalRequestId(managerRecord.mLastSessionCreationRequest
- .mManagerRequestId),
+ toOriginalRequestId(lastRequest.mManagerRequestId),
REASON_UNKNOWN_ERROR);
- managerRecord.mLastSessionCreationRequest = null;
}
managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord,
MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId,
diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp
new file mode 100644
index 0000000..f26a25b
--- /dev/null
+++ b/services/core/java/com/android/server/notification/Android.bp
@@ -0,0 +1,12 @@
+java_aconfig_library {
+ name: "notification_flags_lib",
+ aconfig_declarations: "notification_flags",
+}
+
+aconfig_declarations {
+ name: "notification_flags",
+ package: "com.android.server.notification",
+ srcs: [
+ "flags.aconfig",
+ ],
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
new file mode 100644
index 0000000..c0bc6d1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.notification"
+
+flag {
+ name: "expire_bitmaps"
+ namespace: "systemui"
+ description: "This flag controls removing expired notification bitmaps"
+ bug: "290381858"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 3ba307b..1134714 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -307,7 +307,8 @@
&& mode != BugreportParams.BUGREPORT_MODE_REMOTE
&& mode != BugreportParams.BUGREPORT_MODE_WEAR
&& mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
- && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
+ && mode != BugreportParams.BUGREPORT_MODE_WIFI
+ && mode != BugreportParams.BUGREPORT_MODE_ONBOARDING) {
Slog.w(TAG, "Unknown bugreport mode: " + mode);
throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 31869b8..3e18387 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3546,6 +3546,7 @@
+ " no longer exists; its data will be wiped");
mInjector.getHandler().post(
() -> mRemovePackageHelper.removePackageData(ps, userIds, null, 0, false));
+ expectingBetter.put(ps.getPackageName(), ps.getPath());
} else {
// we still have a disabled system package, but, it still might have
// been removed. check the code path still exists and check there's
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index e73eced..11660a59 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -379,7 +379,12 @@
filter.addDataScheme("package");
mContext.registerReceiverAsUser(mPackageRemovedListener, UserHandle.ALL, filter,
/* broadcastPermission= */ null, mCallbackHandler);
- mPackageMonitor.register(mContext, UserHandle.ALL, mCallbackHandler);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mPackageMonitor.register(mContext, UserHandle.ALL, mCallbackHandler);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
mIsWatchingPackageBroadcasts = true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dc42644..6bcfcfe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6251,7 +6251,11 @@
@Override
public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {
int uid = Binder.getCallingUid();
- mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId, uid);
+ int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), uid,
+ userId, true, true, "registerPackageMonitorCallback",
+ mContext.getPackageName());
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, targetUserId,
+ uid);
}
@Override
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1e6486a..4a4214f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -308,10 +308,8 @@
private final ServiceThread mHandlerThread;
private final Handler mHandler;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- private final BatterySaverController mBatterySaverController;
- private final BatterySaverPolicy mBatterySaverPolicy;
+ @Nullable
private final BatterySaverStateMachine mBatterySaverStateMachine;
- private final BatterySavingStats mBatterySavingStats;
private final LowPowerStandbyController mLowPowerStandbyController;
private final AttentionDetector mAttentionDetector;
private final FaceDownDetector mFaceDownDetector;
@@ -325,6 +323,8 @@
private final PermissionCheckerWrapper mPermissionCheckerWrapper;
private final PowerPropertiesWrapper mPowerPropertiesWrapper;
private final DeviceConfigParameterProvider mDeviceConfigProvider;
+ // True if battery saver is supported on this device.
+ private final boolean mBatterySaverSupported;
private boolean mDisableScreenWakeLocksWhileCached;
@@ -968,20 +968,13 @@
return suspendBlocker;
}
- BatterySaverPolicy createBatterySaverPolicy(
- Object lock, Context context, BatterySavingStats batterySavingStats) {
- return new BatterySaverPolicy(lock, context, batterySavingStats);
- }
-
- BatterySaverController createBatterySaverController(
- Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
- BatterySavingStats batterySavingStats) {
- return new BatterySaverController(lock, context, BackgroundThread.get().getLooper(),
+ BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
+ BatterySavingStats batterySavingStats = new BatterySavingStats(lock);
+ BatterySaverPolicy batterySaverPolicy = new BatterySaverPolicy(lock, context,
+ batterySavingStats);
+ BatterySaverController batterySaverController = new BatterySaverController(lock,
+ context, BackgroundThread.get().getLooper(),
batterySaverPolicy, batterySavingStats);
- }
-
- BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
- BatterySaverController batterySaverController) {
return new BatterySaverStateMachine(lock, context, batterySaverController);
}
@@ -1155,13 +1148,11 @@
mFaceDownDetector = new FaceDownDetector(this::onFlip);
mScreenUndimDetector = new ScreenUndimDetector();
- mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy =
- mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
- mBatterySaverController = mInjector.createBatterySaverController(mLock, mContext,
- mBatterySaverPolicy, mBatterySavingStats);
- mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
- mBatterySaverController);
+ mBatterySaverSupported = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_batterySaverSupported);
+ mBatterySaverStateMachine =
+ mBatterySaverSupported ? mInjector.createBatterySaverStateMachine(mLock, mContext)
+ : null;
mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext,
Looper.getMainLooper());
@@ -1300,7 +1291,9 @@
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
- mBatterySaverStateMachine.onBootCompleted();
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.onBootCompleted();
+ }
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
@@ -1390,8 +1383,9 @@
final ContentResolver resolver = mContext.getContentResolver();
mConstants.start(resolver);
- mBatterySaverController.systemReady();
- mBatterySaverPolicy.systemReady();
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.systemReady();
+ }
mFaceDownDetector.systemReady(mContext);
mScreenUndimDetector.systemReady(mContext);
@@ -2601,7 +2595,10 @@
}
}
- mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel,
+ mBatteryLevelLow);
+ }
}
}
@@ -3554,7 +3551,11 @@
mDozeScreenStateOverrideFromDreamManager,
mDozeScreenBrightnessOverrideFromDreamManagerFloat,
mDrawWakeLockOverrideFromSidekick,
- mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS),
+ mBatterySaverSupported
+ ?
+ mBatterySaverStateMachine.getBatterySaverPolicy()
+ .getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS)
+ : new PowerSaveState.Builder().build(),
sQuiescent, mDozeAfterScreenOff, mBootCompleted,
mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity);
int wakefulness = powerGroup.getWakefulnessLocked();
@@ -3884,7 +3885,7 @@
if (DEBUG) {
Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
}
- if (mIsPowered) {
+ if (mIsPowered || !mBatterySaverSupported) {
return false;
}
@@ -4378,7 +4379,8 @@
private boolean setPowerModeInternal(int mode, boolean enabled) {
// Maybe filter the event.
- if (mode == Mode.LAUNCH && enabled && mBatterySaverController.isLaunchBoostDisabled()) {
+ if (mBatterySaverStateMachine == null || (mode == Mode.LAUNCH && enabled
+ && mBatterySaverStateMachine.getBatterySaverController().isLaunchBoostDisabled())) {
return false;
}
return mNativeWrapper.nativeSetPowerMode(mode, enabled);
@@ -4718,8 +4720,12 @@
pw.println();
pw.println("Display Power: " + mDisplayPowerCallbacks);
- mBatterySaverPolicy.dump(pw);
- mBatterySaverStateMachine.dump(pw);
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.getBatterySaverPolicy().dump(pw);
+ mBatterySaverStateMachine.dump(pw);
+ } else {
+ pw.println("Battery Saver: DISABLED");
+ }
mAttentionDetector.dump(pw);
pw.println();
@@ -5101,8 +5107,10 @@
proto.end(uIDToken);
}
- mBatterySaverStateMachine.dumpProto(proto,
- PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE);
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.dumpProto(proto,
+ PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE);
+ }
mHandler.getLooper().dumpDebug(proto, PowerManagerServiceDumpProto.LOOPER);
@@ -5986,7 +5994,8 @@
public boolean isPowerSaveMode() {
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverController.isEnabled();
+ return mBatterySaverSupported
+ && mBatterySaverStateMachine.getBatterySaverController().isEnabled();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5996,7 +6005,12 @@
public PowerSaveState getPowerSaveState(@ServiceType int serviceType) {
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
+ // Return default PowerSaveState if battery saver is not supported.
+ return mBatterySaverSupported
+ ?
+ mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy(
+ serviceType)
+ : new PowerSaveState.Builder().build();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6017,11 +6031,24 @@
}
}
- @Override // Binder call
- public BatterySaverPolicyConfig getFullPowerSavePolicy() {
+ @Override
+ public boolean isBatterySaverSupported() {
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverStateMachine.getFullBatterySaverPolicy();
+ return mBatterySaverSupported;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public BatterySaverPolicyConfig getFullPowerSavePolicy() {
+ // Return default BatterySaverPolicyConfig if battery saver is not supported.
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mBatterySaverSupported
+ ? mBatterySaverStateMachine.getFullBatterySaverPolicy()
+ : new BatterySaverPolicyConfig.Builder().build();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6036,7 +6063,8 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverStateMachine.setFullBatterySaverPolicy(config);
+ return mBatterySaverSupported
+ && mBatterySaverStateMachine.setFullBatterySaverPolicy(config);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6073,7 +6101,8 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config);
+ return mBatterySaverSupported
+ && mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6088,7 +6117,8 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- return mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled);
+ return mBatterySaverSupported
+ && mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6953,12 +6983,21 @@
@Override
public PowerSaveState getLowPowerState(@ServiceType int serviceType) {
- return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
+ // Return default PowerSaveState if battery saver is not supported.
+ return mBatterySaverSupported
+ ?
+ mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy(
+ serviceType) : new PowerSaveState.Builder().build();
}
@Override
public void registerLowPowerModeObserver(LowPowerModeListener listener) {
- mBatterySaverController.addListener(listener);
+ if (mBatterySaverSupported) {
+ mBatterySaverStateMachine.getBatterySaverController().addListener(listener);
+ } else {
+ Slog.w(TAG,
+ "Battery saver is not supported, no low power mode observer registered");
+ }
}
@Override
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 57d69c2..8c1e9a5 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -40,7 +40,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
-import com.android.server.power.PowerManagerService;
import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener;
import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
import com.android.server.power.batterysaver.BatterySaverPolicy.PolicyLevel;
@@ -223,7 +222,7 @@
}
/**
- * Called by {@link PowerManagerService} on system ready, *with no lock held*.
+ * Called by {@link BatterySaverStateMachine} on system ready, *with no lock held*.
*/
public void systemReady() {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 07d1844..e3f3638 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -41,7 +41,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ConcurrentUtils;
-import com.android.server.power.PowerManagerService;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -283,7 +282,7 @@
}
/**
- * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
+ * Called by {@link BatterySaverStateMachine#systemReady()}, *with no lock held.*
*/
public void systemReady() {
ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index e0bbd36..b22e37b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -44,6 +44,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
import com.android.server.power.BatterySaverStateMachineProto;
+import com.android.server.power.PowerManagerService;
import java.io.PrintWriter;
import java.time.Duration;
@@ -253,6 +254,24 @@
com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
}
+ /**
+ * Called by {@link PowerManagerService} on system ready, *with no lock held*.
+ */
+ public void systemReady() {
+ mBatterySaverController.systemReady();
+ getBatterySaverPolicy().systemReady();
+ }
+
+ /** @return Battery saver controller. */
+ public BatterySaverController getBatterySaverController() {
+ return mBatterySaverController;
+ }
+
+ /** @return Battery saver policy. */
+ public BatterySaverPolicy getBatterySaverPolicy() {
+ return mBatterySaverController.getBatterySaverPolicy();
+ }
+
/** @return true if the automatic percentage based mode should be used */
private boolean isAutomaticModeActiveLocked() {
return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE
@@ -937,8 +956,7 @@
ipw.print(mBatterySaverController.isAdaptiveEnabled());
if (mBatterySaverController.isAdaptiveEnabled()) {
ipw.print(" (advertise=");
- ipw.print(
- mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
+ ipw.print(getBatterySaverPolicy().shouldAdvertiseIsEnabled());
ipw.print(")");
}
ipw.decreaseIndent();
@@ -1005,7 +1023,7 @@
proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED,
mBatterySaverController.isAdaptiveEnabled());
proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED,
- mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
+ getBatterySaverPolicy().shouldAdvertiseIsEnabled());
proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted);
proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded);
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 8dcf3e0..33bed3d 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -171,6 +171,8 @@
public static class NativeWrapper {
private native void nativeInit();
+ private static native long nativeGetHintSessionPreferredRate();
+
private static native long nativeCreateHintSession(int tgid, int uid, int[] tids,
long durationNanos);
@@ -190,13 +192,18 @@
private static native void nativeSetThreads(long halPtr, int[] tids);
- private static native long nativeGetHintSessionPreferredRate();
+ private static native void nativeSetMode(long halPtr, int mode, boolean enabled);
/** Wrapper for HintManager.nativeInit */
public void halInit() {
nativeInit();
}
+ /** Wrapper for HintManager.nativeGetHintSessionPreferredRate */
+ public long halGetHintSessionPreferredRate() {
+ return nativeGetHintSessionPreferredRate();
+ }
+
/** Wrapper for HintManager.nativeCreateHintSession */
public long halCreateHintSession(int tgid, int uid, int[] tids, long durationNanos) {
return nativeCreateHintSession(tgid, uid, tids, durationNanos);
@@ -234,15 +241,16 @@
nativeSendHint(halPtr, hint);
}
- /** Wrapper for HintManager.nativeGetHintSessionPreferredRate */
- public long halGetHintSessionPreferredRate() {
- return nativeGetHintSessionPreferredRate();
- }
-
/** Wrapper for HintManager.nativeSetThreads */
public void halSetThreads(long halPtr, int[] tids) {
nativeSetThreads(halPtr, tids);
}
+
+ /** Wrapper for HintManager.setMode */
+ public void halSetMode(long halPtr, int mode, boolean enabled) {
+ nativeSetMode(halPtr, mode, enabled);
+ }
+
}
@VisibleForTesting
@@ -552,7 +560,7 @@
if (mHalSessionPtr == 0 || !updateHintAllowed()) {
return;
}
- Preconditions.checkArgument(hint >= 0, "the hint ID the hint value should be"
+ Preconditions.checkArgument(hint >= 0, "the hint ID value should be"
+ " greater than zero.");
mNativeWrapper.halSendHint(mHalSessionPtr, hint);
}
@@ -593,6 +601,18 @@
return mThreadIds;
}
+ @Override
+ public void setMode(int mode, boolean enabled) {
+ synchronized (mLock) {
+ if (mHalSessionPtr == 0 || !updateHintAllowed()) {
+ return;
+ }
+ Preconditions.checkArgument(mode >= 0, "the mode Id value should be"
+ + " greater than zero.");
+ mNativeWrapper.halSetMode(mHalSessionPtr, mode, enabled);
+ }
+ }
+
private void onProcStateChanged() {
updateHintAllowed();
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index dc4dc6f..cb09aef 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3436,15 +3436,22 @@
synchronized (mLock) {
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo);
- // catch the use case when a CEC device is unplugged from
- // an HDMI port, then plugged in to the same HDMI port.
- if (mCurrentInputId != null && mCurrentSessionState != null
- && mCurrentInputId.equals(inputInfo.getParentId())
- && inputInfo.getId().equals(mCurrentSessionState.inputId)) {
- logExternalInputEvent(
- FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
- inputInfo.getId(), mCurrentSessionState);
- mCurrentInputId = inputInfo.getId();
+ if (mCurrentInputId != null && mCurrentSessionState != null) {
+ if (TextUtils.equals(mCurrentInputId, inputInfo.getParentId())) {
+ // catch the use case when a CEC device is plugged in an HDMI port,
+ // and TV app does not explicitly call tune() to the added CEC input.
+ logExternalInputEvent(
+ FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
+ inputInfo.getId(), mCurrentSessionState);
+ mCurrentInputId = inputInfo.getId();
+ } else if (TextUtils.equals(mCurrentInputId, inputInfo.getId())) {
+ // catch the use case when a CEC device disconnects itself
+ // and reconnects to update info.
+ logExternalInputEvent(
+ FrameworkStatsLog
+ .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__DEVICE_INFO_UPDATED,
+ mCurrentInputId, mCurrentSessionState);
+ }
}
}
} finally {
diff --git a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
index 9bf046c..3b930f7 100644
--- a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
@@ -28,6 +28,7 @@
import android.os.Looper;
import android.os.Message;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.TimeUtils;
@@ -36,7 +37,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
import java.util.function.Consumer;
import java.util.function.Function;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 59ed3d6..3125518 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2790,6 +2790,9 @@
} else if (isEmbedded()) {
associateStartingWindowWithTaskIfNeeded();
}
+ if (mTransitionController.isCollecting()) {
+ mStartingData.mTransitionId = mTransitionController.getCollectingTransitionId();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 105b2bb..148bf9b 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -16,16 +16,14 @@
package com.android.server.wm;
-import static com.android.server.wm.SnapshotController.ACTIVITY_CLOSE;
-import static com.android.server.wm.SnapshotController.ACTIVITY_OPEN;
-import static com.android.server.wm.SnapshotController.TASK_CLOSE;
-import static com.android.server.wm.SnapshotController.TASK_OPEN;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.os.Environment;
import android.os.SystemProperties;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -35,7 +33,6 @@
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
-import com.android.server.wm.SnapshotController.TransitionState;
import java.io.File;
import java.util.ArrayList;
@@ -62,12 +59,6 @@
static final String SNAPSHOTS_DIRNAME = "activity_snapshots";
/**
- * The pending activities which should capture snapshot when process transition finish.
- */
- @VisibleForTesting
- final ArraySet<ActivityRecord> mPendingCaptureActivity = new ArraySet<>();
-
- /**
* The pending activities which should remove snapshot from memory when process transition
* finish.
*/
@@ -86,6 +77,10 @@
@VisibleForTesting
final ArraySet<ActivityRecord> mPendingLoadActivity = new ArraySet<>();
+ private final ArraySet<ActivityRecord> mOnBackPressedActivities = new ArraySet<>();
+
+ private final ArrayList<ActivityRecord> mTmpBelowActivities = new ArrayList<>();
+ private final ArrayList<WindowContainer> mTmpTransitionParticipants = new ArrayList<>();
private final SnapshotPersistQueue mSnapshotPersistQueue;
private final PersistInfoProvider mPersistInfoProvider;
private final AppSnapshotLoader mSnapshotLoader;
@@ -117,20 +112,6 @@
setSnapshotEnabled(snapshotEnabled);
}
- void systemReady() {
- if (shouldDisableSnapshots()) {
- return;
- }
- mService.mSnapshotController.registerTransitionStateConsumer(
- ACTIVITY_OPEN, this::handleOpenActivityTransition);
- mService.mSnapshotController.registerTransitionStateConsumer(
- ACTIVITY_CLOSE, this::handleCloseActivityTransition);
- mService.mSnapshotController.registerTransitionStateConsumer(
- TASK_OPEN, this::handleOpenTaskTransition);
- mService.mSnapshotController.registerTransitionStateConsumer(
- TASK_CLOSE, this::handleCloseTaskTransition);
- }
-
@Override
protected float initSnapshotScale() {
final float config = mService.mContext.getResources().getFloat(
@@ -173,6 +154,7 @@
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cleanUpUserFiles");
final File file = mPersistInfoProvider.getDirectory(userId);
if (file.exists()) {
final File[] contents = file.listFiles();
@@ -182,15 +164,30 @@
}
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
}
+ void addOnBackPressedActivity(ActivityRecord ar) {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
+ mOnBackPressedActivities.add(ar);
+ }
+
+ void clearOnBackPressedActivities() {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
+ mOnBackPressedActivities.clear();
+ }
+
/**
- * Prepare to handle on transition start. Clear all temporary fields.
+ * Prepare to collect any change for snapshots processing. Clear all temporary fields.
*/
- void preTransitionStart() {
+ void beginSnapshotProcess() {
if (shouldDisableSnapshots()) {
return;
}
@@ -198,18 +195,22 @@
}
/**
- * on transition start has notified, start process data.
+ * End collect any change for snapshots processing, start process data.
*/
- void postTransitionStart() {
+ void endSnapshotProcess() {
if (shouldDisableSnapshots()) {
return;
}
- onCommitTransition();
+ for (int i = mOnBackPressedActivities.size() - 1; i >= 0; --i) {
+ handleActivityTransition(mOnBackPressedActivities.valueAt(i));
+ }
+ mOnBackPressedActivities.clear();
+ mTmpTransitionParticipants.clear();
+ postProcess();
}
@VisibleForTesting
void resetTmpFields() {
- mPendingCaptureActivity.clear();
mPendingRemoveActivity.clear();
mPendingDeleteActivity.clear();
mPendingLoadActivity.clear();
@@ -218,31 +219,13 @@
/**
* Start process all pending activities for a transition.
*/
- private void onCommitTransition() {
+ private void postProcess() {
if (DEBUG) {
- Slog.d(TAG, "ActivitySnapshotController#onCommitTransition result:"
- + " capture " + mPendingCaptureActivity
+ Slog.d(TAG, "ActivitySnapshotController#postProcess result:"
+ " remove " + mPendingRemoveActivity
+ " delete " + mPendingDeleteActivity
+ " load " + mPendingLoadActivity);
}
- // task snapshots
- for (int i = mPendingCaptureActivity.size() - 1; i >= 0; i--) {
- recordSnapshot(mPendingCaptureActivity.valueAt(i));
- }
- // clear mTmpRemoveActivity from cache
- for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) {
- final ActivityRecord ar = mPendingRemoveActivity.valueAt(i);
- final int code = getSystemHashCode(ar);
- mCache.onIdRemoved(code);
- }
- // clear snapshot on cache and delete files
- for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) {
- final ActivityRecord ar = mPendingDeleteActivity.valueAt(i);
- final int code = getSystemHashCode(ar);
- mCache.onIdRemoved(code);
- removeIfUserSavedFileExist(code, ar.mUserId);
- }
// load snapshot to cache
for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) {
final ActivityRecord ar = mPendingLoadActivity.valueAt(i);
@@ -258,6 +241,8 @@
new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) {
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+ "load_activity_snapshot");
final TaskSnapshot snapshot = mSnapshotLoader.loadTask(code,
userId, false /* loadLowResolutionBitmap */);
synchronized (mService.getWindowManagerLock()) {
@@ -265,16 +250,36 @@
mCache.putSnapshot(ar, snapshot);
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
}
}
+ // clear mTmpRemoveActivity from cache
+ for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) {
+ final ActivityRecord ar = mPendingRemoveActivity.valueAt(i);
+ final int code = getSystemHashCode(ar);
+ mCache.onIdRemoved(code);
+ }
+ // clear snapshot on cache and delete files
+ for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) {
+ final ActivityRecord ar = mPendingDeleteActivity.valueAt(i);
+ final int code = getSystemHashCode(ar);
+ mCache.onIdRemoved(code);
+ removeIfUserSavedFileExist(code, ar.mUserId);
+ }
// don't keep any reference
resetTmpFields();
}
- private void recordSnapshot(ActivityRecord activity) {
+ void recordSnapshot(ActivityRecord activity) {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
+ }
final TaskSnapshot snapshot = recordSnapshotInner(activity, false /* allowSnapshotHome */);
if (snapshot != null) {
final int code = getSystemHashCode(activity);
@@ -285,15 +290,20 @@
/**
* Called when the visibility of an app changes outside the regular app transition flow.
*/
- void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
+ void notifyAppVisibilityChanged(ActivityRecord ar, boolean visible) {
if (shouldDisableSnapshots()) {
return;
}
+ final Task task = ar.getTask();
+ if (task == null) {
+ return;
+ }
+ // Doesn't need to capture activity snapshot when it converts from translucent.
if (!visible) {
resetTmpFields();
- addBelowTopActivityIfExist(appWindowToken.getTask(), mPendingRemoveActivity,
+ addBelowActivityIfExist(ar, mPendingRemoveActivity, false,
"remove-snapshot");
- onCommitTransition();
+ postProcess();
}
}
@@ -301,65 +311,146 @@
return System.identityHashCode(activity);
}
- void handleOpenActivityTransition(TransitionState<ActivityRecord> transitionState) {
- ArraySet<ActivityRecord> participant = transitionState.getParticipant(false /* open */);
- for (ActivityRecord ar : participant) {
- mPendingCaptureActivity.add(ar);
- // remove the snapshot for the one below close
- final ActivityRecord below = ar.getTask().getActivityBelow(ar);
- if (below != null) {
- mPendingRemoveActivity.add(below);
+ @VisibleForTesting
+ void handleTransitionFinish(@NonNull ArrayList<WindowContainer> windows) {
+ mTmpTransitionParticipants.clear();
+ mTmpTransitionParticipants.addAll(windows);
+ for (int i = mTmpTransitionParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer next = mTmpTransitionParticipants.get(i);
+ if (next.asTask() != null) {
+ handleTaskTransition(next.asTask());
+ } else if (next.asTaskFragment() != null) {
+ final TaskFragment tf = next.asTaskFragment();
+ final ActivityRecord ar = tf.getTopMostActivity();
+ if (ar != null) {
+ handleActivityTransition(ar);
+ }
+ } else if (next.asActivityRecord() != null) {
+ handleActivityTransition(next.asActivityRecord());
}
}
}
- void handleCloseActivityTransition(TransitionState<ActivityRecord> transitionState) {
- ArraySet<ActivityRecord> participant = transitionState.getParticipant(true /* open */);
- for (ActivityRecord ar : participant) {
+ private void handleActivityTransition(@NonNull ActivityRecord ar) {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
+ if (ar.isVisibleRequested()) {
mPendingDeleteActivity.add(ar);
// load next one if exists.
- final ActivityRecord below = ar.getTask().getActivityBelow(ar);
- if (below != null) {
- mPendingLoadActivity.add(below);
- }
+ addBelowActivityIfExist(ar, mPendingLoadActivity, true, "load-snapshot");
+ } else {
+ // remove the snapshot for the one below close
+ addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot");
}
}
- void handleCloseTaskTransition(TransitionState<Task> closeTaskTransitionRecord) {
- ArraySet<Task> participant = closeTaskTransitionRecord.getParticipant(false /* open */);
- for (Task close : participant) {
- // this is close task transition
- // remove the N - 1 from cache
- addBelowTopActivityIfExist(close, mPendingRemoveActivity, "remove-snapshot");
+ private void handleTaskTransition(Task task) {
+ if (shouldDisableSnapshots()) {
+ return;
}
- }
-
- void handleOpenTaskTransition(TransitionState<Task> openTaskTransitionRecord) {
- ArraySet<Task> participant = openTaskTransitionRecord.getParticipant(true /* open */);
- for (Task open : participant) {
- // this is close task transition
- // remove the N - 1 from cache
- addBelowTopActivityIfExist(open, mPendingLoadActivity, "load-snapshot");
+ final ActivityRecord topActivity = task.getTopMostActivity();
+ if (topActivity == null) {
+ return;
+ }
+ if (task.isVisibleRequested()) {
+ // this is open task transition
+ // load the N - 1 to cache
+ addBelowActivityIfExist(topActivity, mPendingLoadActivity, true, "load-snapshot");
// Move the activities to top of mSavedFilesInOrder, so when purge happen, there
// will trim the persisted files from the most non-accessed.
- adjustSavedFileOrder(open);
+ adjustSavedFileOrder(task);
+ } else {
+ // this is close task transition
+ // remove the N - 1 from cache
+ addBelowActivityIfExist(topActivity, mPendingRemoveActivity, true, "remove-snapshot");
}
}
- // Add the top -1 activity to a set if it exists.
- private void addBelowTopActivityIfExist(Task task, ArraySet<ActivityRecord> set,
- String debugMessage) {
- final ActivityRecord topActivity = task.getTopMostActivity();
- if (topActivity != null) {
- final ActivityRecord below = task.getActivityBelow(topActivity);
- if (below != null) {
- set.add(below);
- if (DEBUG) {
- Slog.d(TAG, "ActivitySnapshotController#addBelowTopActivityIfExist "
- + below + " from " + debugMessage);
- }
+ /**
+ * Add the top -1 activity to a set if it exists.
+ * @param inTransition true if the activity must participant in transition.
+ */
+ private void addBelowActivityIfExist(ActivityRecord currentActivity,
+ ArraySet<ActivityRecord> set, boolean inTransition, String debugMessage) {
+ getActivityBelow(currentActivity, inTransition, mTmpBelowActivities);
+ for (int i = mTmpBelowActivities.size() - 1; i >= 0; --i) {
+ set.add(mTmpBelowActivities.get(i));
+ if (DEBUG) {
+ Slog.d(TAG, "ActivitySnapshotController#addBelowTopActivityIfExist "
+ + mTmpBelowActivities.get(i) + " from " + debugMessage);
}
}
+ mTmpBelowActivities.clear();
+ }
+
+ private void getActivityBelow(ActivityRecord currentActivity, boolean inTransition,
+ ArrayList<ActivityRecord> result) {
+ final Task currentTask = currentActivity.getTask();
+ if (currentTask == null) {
+ return;
+ }
+ final ActivityRecord initPrev = currentTask.getActivityBelow(currentActivity);
+ if (initPrev == null) {
+ return;
+ }
+ final TaskFragment currTF = currentActivity.getTaskFragment();
+ final TaskFragment prevTF = initPrev.getTaskFragment();
+ final TaskFragment prevAdjacentTF = prevTF != null
+ ? prevTF.getAdjacentTaskFragment() : null;
+ if (currTF == prevTF && currTF != null || prevAdjacentTF == null) {
+ // Current activity and previous one is in the same task fragment, or
+ // previous activity is not in a task fragment, or
+ // previous activity's task fragment doesn't adjacent to any others.
+ if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
+ result.add(initPrev);
+ }
+ return;
+ }
+
+ if (prevAdjacentTF == currTF) {
+ // previous activity A is adjacent to current activity B.
+ // Try to find anyone below previous activityA, which are C and D if exists.
+ // A | B
+ // C (| D)
+ getActivityBelow(initPrev, inTransition, result);
+ } else {
+ // previous activity C isn't adjacent to current activity A.
+ // A
+ // B | C
+ final Task prevAdjacentTask = prevAdjacentTF.getTask();
+ if (prevAdjacentTask == currentTask) {
+ final int currentIndex = currTF != null
+ ? currentTask.mChildren.indexOf(currTF)
+ : currentTask.mChildren.indexOf(currentActivity);
+ final int prevAdjacentIndex =
+ prevAdjacentTask.mChildren.indexOf(prevAdjacentTF);
+ // prevAdjacentTF already above currentActivity
+ if (prevAdjacentIndex > currentIndex) {
+ return;
+ }
+ }
+ if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
+ result.add(initPrev);
+ }
+ // prevAdjacentTF is adjacent to another one
+ final ActivityRecord prevAdjacentActivity = prevAdjacentTF.getTopMostActivity();
+ if (prevAdjacentActivity != null && (!inTransition
+ || isInParticipant(prevAdjacentActivity, mTmpTransitionParticipants))) {
+ result.add(prevAdjacentActivity);
+ }
+ }
+ }
+
+ static boolean isInParticipant(ActivityRecord ar,
+ ArrayList<WindowContainer> transitionParticipants) {
+ for (int i = transitionParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = transitionParticipants.get(i);
+ if (ar == wc || ar.isDescendantOf(wc)) {
+ return true;
+ }
+ }
+ return false;
}
private void adjustSavedFileOrder(Task nextTopTask) {
@@ -376,6 +467,9 @@
@Override
void onAppRemoved(ActivityRecord activity) {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
super.onAppRemoved(activity);
final int code = getSystemHashCode(activity);
removeIfUserSavedFileExist(code, activity.mUserId);
@@ -386,6 +480,9 @@
@Override
void onAppDied(ActivityRecord activity) {
+ if (shouldDisableSnapshots()) {
+ return;
+ }
super.onAppDied(activity);
final int code = getSystemHashCode(activity);
removeIfUserSavedFileExist(code, activity.mUserId);
@@ -440,7 +537,7 @@
private void removeIfUserSavedFileExist(int code, int userId) {
final UserSavedFile usf = getUserFiles(userId).get(code);
if (usf != null) {
- mUserSavedFiles.remove(code);
+ mUserSavedFiles.get(userId).remove(code);
mSavedFilesInOrder.remove(usf);
mPersister.removeSnap(code, userId);
}
@@ -490,11 +587,13 @@
new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) {
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activity_remove_files");
for (int i = files.size() - 1; i >= 0; --i) {
final UserSavedFile usf = files.get(i);
mSnapshotPersistQueue.deleteSnapshot(
usf.mFileId, usf.mUserId, mPersistInfoProvider);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8673b90..6c848d1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3029,9 +3029,11 @@
// Set to activity manager directly to make sure the state can be seen by the subsequent
// update of scheduling group.
proc.setRunningAnimationUnsafe();
- mH.removeMessages(H.UPDATE_PROCESS_ANIMATING_STATE, proc);
- mH.sendMessageDelayed(mH.obtainMessage(H.UPDATE_PROCESS_ANIMATING_STATE, proc),
+ mH.sendMessage(mH.obtainMessage(H.ADD_WAKEFULNESS_ANIMATING_REASON, proc));
+ mH.removeMessages(H.REMOVE_WAKEFULNESS_ANIMATING_REASON, proc);
+ mH.sendMessageDelayed(mH.obtainMessage(H.REMOVE_WAKEFULNESS_ANIMATING_REASON, proc),
DOZE_ANIMATING_STATE_RETAIN_TIME_MS);
+ Trace.instant(TRACE_TAG_WINDOW_MANAGER, "requestWakefulnessAnimating");
}
@Override
@@ -5657,9 +5659,10 @@
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
- static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
static final int RESUME_FG_APP_SWITCH_MSG = 4;
+ static final int ADD_WAKEFULNESS_ANIMATING_REASON = 5;
+ static final int REMOVE_WAKEFULNESS_ANIMATING_REASON = 6;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5676,13 +5679,23 @@
tracker.deliverResult(mContext);
}
break;
- case UPDATE_PROCESS_ANIMATING_STATE: {
+ case ADD_WAKEFULNESS_ANIMATING_REASON: {
final WindowProcessController proc = (WindowProcessController) msg.obj;
synchronized (mGlobalLock) {
- proc.updateRunningRemoteOrRecentsAnimation();
+ proc.addAnimatingReason(
+ WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
}
}
break;
+ case REMOVE_WAKEFULNESS_ANIMATING_REASON: {
+ final WindowProcessController proc = (WindowProcessController) msg.obj;
+ synchronized (mGlobalLock) {
+ proc.removeAnimatingReason(
+ WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
+ }
+ Trace.instant(TRACE_TAG_WINDOW_MANAGER, "finishWakefulnessAnimating");
+ }
+ break;
case END_POWER_MODE_UNKNOWN_VISIBILITY_MSG: {
synchronized (mGlobalLock) {
mRetainPowerModeAndTopProcessState = false;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 976641b..993c016 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1177,6 +1177,8 @@
if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
return null;
}
+ mCloseTarget.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.clearOnBackPressedActivities();
applyPreviewStrategy(mOpenAdaptor, openActivity);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
@@ -1222,6 +1224,8 @@
// Call it again to make sure the activity could be visible while handling the pending
// animation.
activity.commitVisibility(true, true);
+ activity.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.addOnBackPressedActivity(activity);
}
activity.mLaunchTaskBehind = true;
@@ -1248,6 +1252,9 @@
// Restore the launch-behind state.
activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
activity.mLaunchTaskBehind = false;
+ // Ignore all change
+ activity.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.clearOnBackPressedActivities();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
activity);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 20595ea..73fdfe0 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.gui.StalledTransactionInfo;
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
@@ -96,7 +97,7 @@
@Override
public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(
- timeoutMessage("Application does not have a focused window"));
+ timeoutMessage(OptionalInt.empty(), "Application does not have a focused window"));
mService.mAnrController.notifyAppUnresponsive(applicationHandle, timeoutRecord);
}
@@ -104,7 +105,7 @@
public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
String reason) {
TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
- timeoutMessage(reason));
+ timeoutMessage(pid, reason));
mService.mAnrController.notifyWindowUnresponsive(token, pid, timeoutRecord);
}
@@ -354,11 +355,21 @@
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
- private String timeoutMessage(String reason) {
- if (reason == null) {
- return "Input dispatching timed out";
+ private String timeoutMessage(OptionalInt pid, String reason) {
+ String message = (reason == null) ? "Input dispatching timed out."
+ : String.format("Input dispatching timed out (%s).", reason);
+ if (pid.isEmpty()) {
+ return message;
}
- return "Input dispatching timed out (" + reason + ")";
+ StalledTransactionInfo stalledTransactionInfo =
+ SurfaceControl.getStalledTransactionInfo(pid.getAsInt());
+ if (stalledTransactionInfo == null) {
+ return message;
+ }
+ return String.format("%s Buffer processing for the associated surface is stuck due to an "
+ + "unsignaled fence (window=%s, bufferId=0x%016X, frameNumber=%s). This "
+ + "potentially indicates a GPU hang.", message, stalledTransactionInfo.layerName,
+ stalledTransactionInfo.bufferId, stalledTransactionInfo.frameNumber);
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3551370..f9fa9e6 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -20,7 +20,6 @@
import static android.view.SurfaceControl.HIDDEN;
import static android.window.TaskConstants.TASK_CHILD_LAYER_LETTERBOX_BACKGROUND;
-import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -256,11 +255,11 @@
private final GestureDetector mDoubleTapDetector;
private final DoubleTapListener mDoubleTapListener;
- TapEventReceiver(InputChannel inputChannel, Context context) {
+ TapEventReceiver(InputChannel inputChannel, WindowManagerService wmService) {
super(inputChannel, UiThread.getHandler().getLooper());
- mDoubleTapListener = new DoubleTapListener();
+ mDoubleTapListener = new DoubleTapListener(wmService);
mDoubleTapDetector = new GestureDetector(
- context, mDoubleTapListener, UiThread.getHandler());
+ wmService.mContext, mDoubleTapListener, UiThread.getHandler());
}
@Override
@@ -271,14 +270,24 @@
}
private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+ private final WindowManagerService mWmService;
+
+ private DoubleTapListener(WindowManagerService wmService) {
+ mWmService = wmService;
+ }
+
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_UP) {
- mDoubleTapCallbackX.accept((int) e.getRawX());
- mDoubleTapCallbackY.accept((int) e.getRawY());
- return true;
+ synchronized (mWmService.mGlobalLock) {
+ // This check prevents late events to be handled in case the Letterbox has been
+ // already destroyed and so mOuter.isEmpty() is true.
+ if (!mOuter.isEmpty() && e.getAction() == MotionEvent.ACTION_UP) {
+ mDoubleTapCallbackX.accept((int) e.getRawX());
+ mDoubleTapCallbackY.accept((int) e.getRawY());
+ return true;
+ }
+ return false;
}
- return false;
}
}
@@ -294,7 +303,7 @@
mWmService = win.mWmService;
final String name = namePrefix + (win.mActivityRecord != null ? win.mActivityRecord : win);
mClientChannel = mWmService.mInputManager.createInputChannel(name);
- mInputEventReceiver = new TapEventReceiver(mClientChannel, mWmService.mContext);
+ mInputEventReceiver = new TapEventReceiver(mClientChannel, mWmService);
mToken = mClientChannel.getToken();
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index badcfa9..37f9730 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -16,185 +16,36 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import android.annotation.IntDef;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
+import android.os.Trace;
import android.view.WindowManager;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.function.Consumer;
/**
* Integrates common functionality from TaskSnapshotController and ActivitySnapshotController.
*/
class SnapshotController {
- private static final boolean DEBUG = false;
- private static final String TAG = AbsAppSnapshotController.TAG;
-
- static final int ACTIVITY_OPEN = 1;
- static final int ACTIVITY_CLOSE = 2;
- static final int TASK_OPEN = 4;
- static final int TASK_CLOSE = 8;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- value = {ACTIVITY_OPEN,
- ACTIVITY_CLOSE,
- TASK_OPEN,
- TASK_CLOSE})
- @interface TransitionStateType {}
-
private final SnapshotPersistQueue mSnapshotPersistQueue;
final TaskSnapshotController mTaskSnapshotController;
final ActivitySnapshotController mActivitySnapshotController;
- private final ArraySet<Task> mTmpCloseTasks = new ArraySet<>();
- private final ArraySet<Task> mTmpOpenTasks = new ArraySet<>();
-
- private final SparseArray<TransitionState> mTmpOpenCloseRecord = new SparseArray<>();
- private final ArraySet<Integer> mTmpAnalysisRecord = new ArraySet<>();
- private final SparseArray<ArrayList<Consumer<TransitionState>>> mTransitionStateConsumer =
- new SparseArray<>();
- private int mActivatedType;
-
- private final ActivityOrderCheck mActivityOrderCheck = new ActivityOrderCheck();
- private final ActivityOrderCheck.AnalysisResult mResultHandler = (type, close, open) -> {
- addTransitionRecord(type, true/* open */, open);
- addTransitionRecord(type, false/* open */, close);
- };
-
- private static class ActivityOrderCheck {
- private ActivityRecord mOpenActivity;
- private ActivityRecord mCloseActivity;
- private int mOpenIndex = -1;
- private int mCloseIndex = -1;
-
- private void reset() {
- mOpenActivity = null;
- mCloseActivity = null;
- mOpenIndex = -1;
- mCloseIndex = -1;
- }
-
- private void setTarget(boolean open, ActivityRecord ar, int index) {
- if (open) {
- mOpenActivity = ar;
- mOpenIndex = index;
- } else {
- mCloseActivity = ar;
- mCloseIndex = index;
- }
- }
-
- void analysisOrder(ArraySet<ActivityRecord> closeApps,
- ArraySet<ActivityRecord> openApps, Task task, AnalysisResult result) {
- for (int j = closeApps.size() - 1; j >= 0; j--) {
- final ActivityRecord ar = closeApps.valueAt(j);
- if (ar.getTask() == task) {
- setTarget(false, ar, task.mChildren.indexOf(ar));
- break;
- }
- }
- for (int j = openApps.size() - 1; j >= 0; j--) {
- final ActivityRecord ar = openApps.valueAt(j);
- if (ar.getTask() == task) {
- setTarget(true, ar, task.mChildren.indexOf(ar));
- break;
- }
- }
- if (mOpenIndex > mCloseIndex && mCloseIndex != -1) {
- result.onCheckResult(ACTIVITY_OPEN, mCloseActivity, mOpenActivity);
- } else if (mOpenIndex < mCloseIndex && mOpenIndex != -1) {
- result.onCheckResult(ACTIVITY_CLOSE, mCloseActivity, mOpenActivity);
- }
- reset();
- }
- private interface AnalysisResult {
- void onCheckResult(@TransitionStateType int type,
- ActivityRecord close, ActivityRecord open);
- }
- }
-
- private void addTransitionRecord(int type, boolean open, WindowContainer target) {
- TransitionState record = mTmpOpenCloseRecord.get(type);
- if (record == null) {
- record = new TransitionState();
- mTmpOpenCloseRecord.set(type, record);
- }
- record.addParticipant(target, open);
- mTmpAnalysisRecord.add(type);
- }
-
- private void clearRecord() {
- mTmpOpenCloseRecord.clear();
- mTmpAnalysisRecord.clear();
- }
-
- static class TransitionState<TYPE extends WindowContainer> {
- private final ArraySet<TYPE> mOpenParticipant = new ArraySet<>();
- private final ArraySet<TYPE> mCloseParticipant = new ArraySet<>();
-
- void addParticipant(TYPE target, boolean open) {
- final ArraySet<TYPE> participant = open
- ? mOpenParticipant : mCloseParticipant;
- participant.add(target);
- }
-
- ArraySet<TYPE> getParticipant(boolean open) {
- return open ? mOpenParticipant : mCloseParticipant;
- }
- }
-
SnapshotController(WindowManagerService wms) {
mSnapshotPersistQueue = new SnapshotPersistQueue();
mTaskSnapshotController = new TaskSnapshotController(wms, mSnapshotPersistQueue);
mActivitySnapshotController = new ActivitySnapshotController(wms, mSnapshotPersistQueue);
}
- void registerTransitionStateConsumer(@TransitionStateType int type,
- Consumer<TransitionState> consumer) {
- ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type);
- if (consumers == null) {
- consumers = new ArrayList<>();
- mTransitionStateConsumer.set(type, consumers);
- }
- if (!consumers.contains(consumer)) {
- consumers.add(consumer);
- }
- mActivatedType |= type;
- }
-
- void unregisterTransitionStateConsumer(int type, Consumer<TransitionState> consumer) {
- final ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type);
- if (consumers == null) {
- return;
- }
- consumers.remove(consumer);
- if (consumers.size() == 0) {
- mActivatedType &= ~type;
- }
- }
-
- private boolean hasTransitionStateConsumer(@TransitionStateType int type) {
- return (mActivatedType & type) != 0;
- }
-
void systemReady() {
mSnapshotPersistQueue.systemReady();
- mTaskSnapshotController.systemReady();
- mActivitySnapshotController.systemReady();
}
void setPause(boolean paused) {
@@ -212,47 +63,69 @@
}
void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
- if (!visible && hasTransitionStateConsumer(TASK_CLOSE)) {
- final Task task = appWindowToken.getTask();
- if (task == null || task.isVisibleRequested()) {
- return;
+ mActivitySnapshotController.notifyAppVisibilityChanged(appWindowToken, visible);
+ }
+
+ // For legacy transition, which won't support activity snapshot
+ void onTransitionStarting(DisplayContent displayContent) {
+ mTaskSnapshotController.handleClosingApps(displayContent.mClosingApps);
+ }
+
+ // For shell transition, record snapshots before transaction start.
+ void onTransactionReady(@WindowManager.TransitionType int type,
+ ArrayList<Transition.ChangeInfo> changeInfos) {
+ final boolean isTransitionOpen = isTransitionOpen(type);
+ final boolean isTransitionClose = isTransitionClose(type);
+ if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM) {
+ return;
+ }
+ for (int i = changeInfos.size() - 1; i >= 0; --i) {
+ Transition.ChangeInfo info = changeInfos.get(i);
+ // Intentionally skip record snapshot for changes originated from PiP.
+ if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue;
+ if (info.mContainer.asTask() != null && !info.mContainer.isVisibleRequested()) {
+ mTaskSnapshotController.recordSnapshot(info.mContainer.asTask(),
+ false /* allowSnapshotHome */);
}
- // close task transition
- addTransitionRecord(TASK_CLOSE, false /*open*/, task);
- mActivitySnapshotController.preTransitionStart();
- notifyTransition(TASK_CLOSE);
- mActivitySnapshotController.postTransitionStart();
- clearRecord();
+ // Won't need to capture activity snapshot in close transition.
+ if (isTransitionClose) {
+ continue;
+ }
+ if (info.mContainer.asActivityRecord() != null
+ || info.mContainer.asTaskFragment() != null) {
+ final TaskFragment tf = info.mContainer.asTaskFragment();
+ final ActivityRecord ar = tf != null ? tf.getTopMostActivity()
+ : info.mContainer.asActivityRecord();
+ final boolean taskVis = ar != null && ar.getTask().isVisibleRequested();
+ if (ar != null && !ar.isVisibleRequested() && taskVis) {
+ mActivitySnapshotController.recordSnapshot(ar);
+ }
+ }
}
}
- // For legacy transition
- void onTransitionStarting(DisplayContent displayContent) {
- handleAppTransition(displayContent.mClosingApps, displayContent.mOpeningApps);
- }
-
- // For shell transition, adapt to legacy transition.
- void onTransitionReady(@WindowManager.TransitionType int type,
- ArraySet<WindowContainer> participants) {
+ void onTransitionFinish(@WindowManager.TransitionType int type,
+ ArrayList<Transition.ChangeInfo> changeInfos) {
final boolean isTransitionOpen = isTransitionOpen(type);
final boolean isTransitionClose = isTransitionClose(type);
if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM
- || (mActivatedType == 0)) {
+ || (changeInfos.isEmpty())) {
return;
}
- final ArraySet<ActivityRecord> openingApps = new ArraySet<>();
- final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
-
- for (int i = participants.size() - 1; i >= 0; --i) {
- final ActivityRecord ar = participants.valueAt(i).asActivityRecord();
- if (ar == null || ar.getTask() == null) continue;
- if (ar.isVisibleRequested()) {
- openingApps.add(ar);
- } else {
- closingApps.add(ar);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SnapshotController_analysis");
+ mActivitySnapshotController.beginSnapshotProcess();
+ final ArrayList<WindowContainer> windows = new ArrayList<>();
+ for (int i = changeInfos.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = changeInfos.get(i).mContainer;
+ if (wc.asTask() == null && wc.asTaskFragment() == null
+ && wc.asActivityRecord() == null) {
+ continue;
}
+ windows.add(wc);
}
- handleAppTransition(closingApps, openingApps);
+ mActivitySnapshotController.handleTransitionFinish(windows);
+ mActivitySnapshotController.endSnapshotProcess();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
private static boolean isTransitionOpen(int type) {
@@ -262,78 +135,6 @@
return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
}
- @VisibleForTesting
- void handleAppTransition(ArraySet<ActivityRecord> closingApps,
- ArraySet<ActivityRecord> openApps) {
- if (mActivatedType == 0) {
- return;
- }
- analysisTransition(closingApps, openApps);
- mActivitySnapshotController.preTransitionStart();
- for (Integer transitionType : mTmpAnalysisRecord) {
- notifyTransition(transitionType);
- }
- mActivitySnapshotController.postTransitionStart();
- clearRecord();
- }
-
- private void notifyTransition(int transitionType) {
- final TransitionState record = mTmpOpenCloseRecord.get(transitionType);
- final ArrayList<Consumer<TransitionState>> consumers =
- mTransitionStateConsumer.get(transitionType);
- for (Consumer<TransitionState> consumer : consumers) {
- consumer.accept(record);
- }
- }
-
- private void analysisTransition(ArraySet<ActivityRecord> closingApps,
- ArraySet<ActivityRecord> openingApps) {
- getParticipantTasks(closingApps, mTmpCloseTasks, false /* isOpen */);
- getParticipantTasks(openingApps, mTmpOpenTasks, true /* isOpen */);
- if (DEBUG) {
- Slog.d(TAG, "AppSnapshotController#analysisTransition participants"
- + " mTmpCloseTasks " + mTmpCloseTasks
- + " mTmpOpenTasks " + mTmpOpenTasks);
- }
- for (int i = mTmpCloseTasks.size() - 1; i >= 0; i--) {
- final Task closeTask = mTmpCloseTasks.valueAt(i);
- if (mTmpOpenTasks.contains(closeTask)) {
- if (hasTransitionStateConsumer(ACTIVITY_OPEN)
- || hasTransitionStateConsumer(ACTIVITY_CLOSE)) {
- mActivityOrderCheck.analysisOrder(closingApps, openingApps, closeTask,
- mResultHandler);
- }
- } else if (hasTransitionStateConsumer(TASK_CLOSE)) {
- // close task transition
- addTransitionRecord(TASK_CLOSE, false /*open*/, closeTask);
- }
- }
- if (hasTransitionStateConsumer(TASK_OPEN)) {
- for (int i = mTmpOpenTasks.size() - 1; i >= 0; i--) {
- final Task openTask = mTmpOpenTasks.valueAt(i);
- if (!mTmpCloseTasks.contains(openTask)) {
- // this is open task transition
- addTransitionRecord(TASK_OPEN, true /*open*/, openTask);
- }
- }
- }
- mTmpCloseTasks.clear();
- mTmpOpenTasks.clear();
- }
-
- private void getParticipantTasks(ArraySet<ActivityRecord> activityRecords, ArraySet<Task> tasks,
- boolean isOpen) {
- for (int i = activityRecords.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = activityRecords.valueAt(i);
- final Task task = activity.getTask();
- if (task == null) continue;
-
- if (isOpen == activity.isVisibleRequested()) {
- tasks.add(task);
- }
- }
- }
-
void dump(PrintWriter pw, String prefix) {
mTaskSnapshotController.dump(pw, prefix);
mActivitySnapshotController.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 58e1c54..f4f641f 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.graphics.Bitmap.CompressFormat.JPEG;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -26,6 +27,7 @@
import android.graphics.Bitmap;
import android.os.Process;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.AtomicFile;
import android.util.Slog;
import android.window.TaskSnapshot;
@@ -249,6 +251,7 @@
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem");
if (!mPersistInfoProvider.createDirectory(mUserId)) {
Slog.e(TAG, "Unable to create snapshot directory for user dir="
+ mPersistInfoProvider.getDirectory(mUserId));
@@ -263,6 +266,7 @@
if (failed) {
deleteSnapshot(mId, mUserId, mPersistInfoProvider);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
boolean writeProto() {
@@ -373,7 +377,9 @@
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DeleteWriteQueueItem");
deleteSnapshot(mId, mUserId, mPersistInfoProvider);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 2b22d75..34806bd 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -65,6 +65,9 @@
/** Whether to prepare the removal animation. */
boolean mPrepareRemoveAnimation;
+ /** Non-zero if this starting window is added in a collecting transition. */
+ int mTransitionId;
+
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c99291d..69eddb9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2878,8 +2878,8 @@
// No need to check if allowed if it's leaving dragResize
if (dragResizing
&& !(getRootTask().getWindowingMode() == WINDOWING_MODE_FREEFORM)) {
- throw new IllegalArgumentException("Drag resize not allow for root task id="
- + getRootTaskId());
+ Slog.e(TAG, "Drag resize isn't allowed for root task id=" + getRootTaskId());
+ return;
}
mDragResizing = dragResizing;
resetDragResizingChangeReported();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index c747c09..4eb4290 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static com.android.server.wm.SnapshotController.TASK_CLOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -77,13 +76,6 @@
setSnapshotEnabled(snapshotEnabled);
}
- void systemReady() {
- if (!shouldDisableSnapshots()) {
- mService.mSnapshotController.registerTransitionStateConsumer(TASK_CLOSE,
- this::handleTaskClose);
- }
- }
-
static PersistInfoProvider createPersistInfoProvider(WindowManagerService service,
BaseAppSnapshotPersister.DirectoryResolver resolver) {
final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
@@ -116,20 +108,23 @@
enableLowResSnapshots, lowResScaleFactor, use16BitFormat);
}
- void handleTaskClose(SnapshotController.TransitionState<Task> closeTaskTransitionRecord) {
+ // Still needed for legacy transition.(AppTransitionControllerTest)
+ void handleClosingApps(ArraySet<ActivityRecord> closingApps) {
if (shouldDisableSnapshots()) {
return;
}
+ // We need to take a snapshot of the task if and only if all activities of the task are
+ // either closing or hidden.
mTmpTasks.clear();
- final ArraySet<Task> tasks = closeTaskTransitionRecord.getParticipant(false /* open */);
- if (mService.mAtmService.getTransitionController().isShellTransitionsEnabled()) {
- mTmpTasks.addAll(tasks);
- } else {
- for (Task task : tasks) {
- getClosingTasksInner(task, mTmpTasks);
- }
+ for (int i = closingApps.size() - 1; i >= 0; i--) {
+ final ActivityRecord activity = closingApps.valueAt(i);
+ final Task task = activity.getTask();
+ if (task == null) continue;
+
+ getClosingTasksInner(task, mTmpTasks);
}
snapshotTasks(mTmpTasks);
+ mTmpTasks.clear();
mSkipClosingAppSnapshotTasks.clear();
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index cd15119..3e8c017 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
+import android.os.Trace;
import android.util.ArraySet;
import android.window.TaskSnapshot;
@@ -102,6 +105,7 @@
@Override
void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RemoveObsoleteFilesQueueItem");
final ArraySet<Integer> newPersistedTaskIds;
synchronized (mLock) {
newPersistedTaskIds = new ArraySet<>(mPersistedTaskIdsSinceLastRemoveObsolete);
@@ -120,6 +124,7 @@
}
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index aad17aa..1566bb2c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1191,8 +1191,6 @@
" Skipping post-transition snapshot for task %d",
task.mTaskId);
}
- snapController.mActivitySnapshotController
- .notifyAppVisibilityChanged(ar, false /* visible */);
}
ar.commitVisibility(false /* visible */, false /* performLayout */,
true /* fromTransition */);
@@ -1389,6 +1387,7 @@
// Handle back animation if it's already started.
mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
mController.mFinishingTransition = null;
+ mController.mSnapshotController.onTransitionFinish(mType, mTargets);
}
void abort() {
@@ -1593,16 +1592,7 @@
// transferred. If transition is transient, IME won't be moved during the transition and
// the tasks are still live, so we take the snapshot at the end of the transition instead.
if (mTransientLaunches == null) {
- for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar == null || ar.getTask() == null
- || ar.getTask().isVisibleRequested()) continue;
- final ChangeInfo change = mChanges.get(ar);
- // Intentionally skip record snapshot for changes originated from PiP.
- if (change != null && change.mWindowingMode == WINDOWING_MODE_PINNED) continue;
- mController.mSnapshotController.mTaskSnapshotController.recordSnapshot(
- ar.getTask(), false /* allowSnapshotHome */);
- }
+ mController.mSnapshotController.onTransactionReady(mType, mTargets);
}
// This is non-null only if display has changes. It handles the visible windows that don't
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
index af8fb02..c59d2d3 100644
--- a/services/core/java/com/android/server/wm/TransitionTracer.java
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -145,6 +145,27 @@
}
}
+ void logRemovingStartingWindow(@NonNull StartingData startingData) {
+ if (startingData.mTransitionId == 0) {
+ return;
+ }
+ try {
+ final ProtoOutputStream outputStream = new ProtoOutputStream(CHUNK_SIZE);
+ final long protoToken = outputStream
+ .start(com.android.server.wm.shell.TransitionTraceProto.TRANSITIONS);
+ outputStream.write(com.android.server.wm.shell.Transition.ID,
+ startingData.mTransitionId);
+ outputStream.write(
+ com.android.server.wm.shell.Transition.STARTING_WINDOW_REMOVE_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ outputStream.end(protoToken);
+
+ mTraceBuffer.add(outputStream);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unexpected exception thrown while logging transitions", e);
+ }
+ }
+
private void dumpTransitionTargetsToProto(ProtoOutputStream outputStream,
Transition transition, ArrayList<ChangeInfo> targets) {
Trace.beginSection("TransitionTracer#dumpTransitionTargetsToProto");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 261d6bc..03efb1b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4648,7 +4648,14 @@
void reportSystemGestureExclusionChanged(Session session, IWindow window,
List<Rect> exclusionRects) {
synchronized (mGlobalLock) {
- final WindowState win = windowForClientLocked(session, window, true);
+ final WindowState win = windowForClientLocked(session, window,
+ false /* throwOnError */);
+ if (win == null) {
+ Slog.i(TAG_WM,
+ "reportSystemGestureExclusionChanged(): No window state for package:"
+ + session.mPackageName);
+ return;
+ }
if (win.setSystemGestureExclusion(exclusionRects)) {
win.getDisplayContent().updateSystemGestureExclusion();
}
@@ -4658,7 +4665,14 @@
void reportKeepClearAreasChanged(Session session, IWindow window,
List<Rect> restricted, List<Rect> unrestricted) {
synchronized (mGlobalLock) {
- final WindowState win = windowForClientLocked(session, window, true);
+ final WindowState win = windowForClientLocked(session, window,
+ false /* throwOnError */);
+ if (win == null) {
+ Slog.i(TAG_WM,
+ "reportKeepClearAreasChanged(): No window state for package:"
+ + session.mPackageName);
+ return;
+ }
if (win.setKeepClearAreas(restricted, unrestricted)) {
win.getDisplayContent().updateKeepClearAreas();
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 427ab7e..6d7e297 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1657,9 +1657,18 @@
}
private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
- final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
- final TaskFragment root2 =
- WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment();
+ final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer());
+ if (wc1 == null || !wc1.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final TaskFragment root1 = wc1.asTaskFragment();
+ final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot());
+ if (wc2 == null || !wc2.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final TaskFragment root2 = wc2.asTaskFragment();
if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
@@ -1672,7 +1681,12 @@
}
private int clearAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
- final TaskFragment root = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+ return TRANSACT_EFFECTS_NONE;
+ }
+ final TaskFragment root = wc.asTaskFragment();
if (!root.mCreatedByOrganizer) {
throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by"
+ " organizer root=" + root);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 83e8646..e769a27 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -48,6 +48,7 @@
import static java.util.Objects.requireNonNull;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -76,7 +77,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.IRemoteAnimationRunner;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -88,6 +88,8 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -250,11 +252,30 @@
@Nullable
private ArrayMap<ActivityRecord, int[]> mRemoteActivities;
- /** Whether our process is currently running a {@link RecentsAnimation} */
- private boolean mRunningRecentsAnimation;
+ /**
+ * It can be set for a running transition player ({@link android.window.ITransitionPlayer}) or
+ * remote animators (running {@link android.window.IRemoteTransition}).
+ */
+ static final int ANIMATING_REASON_REMOTE_ANIMATION = 1;
+ /** It is set for wakefulness transition. */
+ static final int ANIMATING_REASON_WAKEFULNESS_CHANGE = 1 << 1;
+ /** Whether the legacy {@link RecentsAnimation} is running. */
+ static final int ANIMATING_REASON_LEGACY_RECENT_ANIMATION = 1 << 2;
- /** Whether our process is currently running a {@link IRemoteAnimationRunner} */
- private boolean mRunningRemoteAnimation;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ANIMATING_REASON_REMOTE_ANIMATION,
+ ANIMATING_REASON_WAKEFULNESS_CHANGE,
+ ANIMATING_REASON_LEGACY_RECENT_ANIMATION,
+ })
+ @interface AnimatingReason {}
+
+ /**
+ * Non-zero if this process is currently running an important animation. This should be never
+ * set for system server.
+ */
+ @AnimatingReason
+ private int mAnimatingReasons;
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
@@ -1869,30 +1890,45 @@
}
void setRunningRecentsAnimation(boolean running) {
- if (mRunningRecentsAnimation == running) {
- return;
+ if (running) {
+ addAnimatingReason(ANIMATING_REASON_LEGACY_RECENT_ANIMATION);
+ } else {
+ removeAnimatingReason(ANIMATING_REASON_LEGACY_RECENT_ANIMATION);
}
- mRunningRecentsAnimation = running;
- updateRunningRemoteOrRecentsAnimation();
}
void setRunningRemoteAnimation(boolean running) {
- if (mRunningRemoteAnimation == running) {
- return;
+ if (running) {
+ addAnimatingReason(ANIMATING_REASON_REMOTE_ANIMATION);
+ } else {
+ removeAnimatingReason(ANIMATING_REASON_REMOTE_ANIMATION);
}
- mRunningRemoteAnimation = running;
- updateRunningRemoteOrRecentsAnimation();
}
- void updateRunningRemoteOrRecentsAnimation() {
+ void addAnimatingReason(@AnimatingReason int reason) {
+ final int prevReasons = mAnimatingReasons;
+ mAnimatingReasons |= reason;
+ if (prevReasons == 0) {
+ setAnimating(true);
+ }
+ }
+
+ void removeAnimatingReason(@AnimatingReason int reason) {
+ final int prevReasons = mAnimatingReasons;
+ mAnimatingReasons &= ~reason;
+ if (prevReasons != 0 && mAnimatingReasons == 0) {
+ setAnimating(false);
+ }
+ }
+
+ /** Applies the animating state to activity manager for updating process priority. */
+ private void setAnimating(boolean animating) {
// Posting on handler so WM lock isn't held when we call into AM.
- mAtm.mH.sendMessage(PooledLambda.obtainMessage(
- WindowProcessListener::setRunningRemoteAnimation, mListener,
- isRunningRemoteTransition()));
+ mAtm.mH.post(() -> mListener.setRunningRemoteAnimation(animating));
}
boolean isRunningRemoteTransition() {
- return mRunningRecentsAnimation || mRunningRemoteAnimation;
+ return (mAnimatingReasons & ANIMATING_REASON_REMOTE_ANIMATION) != 0;
}
/** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
@@ -1946,6 +1982,21 @@
pw.println(prefix + " mLastReportedConfiguration=" + (mHasCachedConfiguration
? ("(cached) " + mLastReportedConfiguration) : mLastReportedConfiguration));
+ final int animatingReasons = mAnimatingReasons;
+ if (animatingReasons != 0) {
+ pw.print(prefix + " mAnimatingReasons=");
+ if ((animatingReasons & ANIMATING_REASON_REMOTE_ANIMATION) != 0) {
+ pw.print("remote-animation|");
+ }
+ if ((animatingReasons & ANIMATING_REASON_WAKEFULNESS_CHANGE) != 0) {
+ pw.print("wakefulness|");
+ }
+ if ((animatingReasons & ANIMATING_REASON_LEGACY_RECENT_ANIMATION) != 0) {
+ pw.print("legacy-recents");
+ }
+ pw.println();
+ }
+
final int stateFlags = mActivityStateFlags;
if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) {
pw.print(prefix + " mActivityStateFlags=");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fc49700..5a45fe1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2412,7 +2412,7 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"removeIfPossible: %s callers=%s", this, Debug.getCallers(5));
- final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
+ final boolean startingWindow = mStartingData != null;
if (startingWindow) {
ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
// Cancel the remove starting window animation on shell. The main window might changed
@@ -2426,6 +2426,7 @@
return false;
}, true);
}
+ mTransitionController.mTransitionTracer.logRemovingStartingWindow(mStartingData);
} else if (mAttrs.type == TYPE_BASE_APPLICATION
&& isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
// Cancel the remove starting window animation in case the binder dead before remove
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 101af4d..405b133 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -94,7 +94,10 @@
cc_defaults {
name: "libservices.core-libs",
- defaults: ["android.hardware.graphics.common-ndk_shared"],
+ defaults: [
+ "android.hardware.graphics.common-ndk_shared",
+ "android.hardware.power-ndk_shared",
+ ],
shared_libs: [
"libadb_pairing_server",
"libadb_pairing_connection",
@@ -177,7 +180,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index ad098b7..4cd018b 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -144,12 +144,20 @@
}
uinput_abs_setup slotAbsSetup;
slotAbsSetup.code = ABS_MT_SLOT;
- slotAbsSetup.absinfo.maximum = MAX_POINTERS;
+ slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
slotAbsSetup.absinfo.minimum = 0;
if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
return invalidFd();
}
+ uinput_abs_setup trackingIdAbsSetup;
+ trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
+ trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ trackingIdAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
+ return invalidFd();
+ }
}
if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
ALOGE("Error creating uinput device: %s", strerror(errno));
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index e148b94..7edf445 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -31,6 +31,7 @@
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
using aidl::android::hardware::power::WorkDuration;
using android::base::StringPrintf;
@@ -41,6 +42,15 @@
static std::unordered_map<jlong, std::shared_ptr<IPowerHintSession>> gSessionMap;
static std::mutex gSessionMapLock;
+static int64_t getHintSessionPreferredRate() {
+ int64_t rate = -1;
+ auto result = gPowerHalController.getHintSessionPreferredRate();
+ if (result.isOk()) {
+ rate = result.value();
+ }
+ return rate;
+}
+
static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid,
std::vector<int32_t> threadIds, int64_t durationNanos) {
auto result = gPowerHalController.createHintSession(tgid, uid, threadIds, durationNanos);
@@ -93,13 +103,9 @@
appSession->setThreads(threadIds);
}
-static int64_t getHintSessionPreferredRate() {
- int64_t rate = -1;
- auto result = gPowerHalController.getHintSessionPreferredRate();
- if (result.isOk()) {
- rate = result.value();
- }
- return rate;
+static void setMode(int64_t session_ptr, SessionMode mode, bool enabled) {
+ auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ appSession->setMode(mode, enabled);
}
// ----------------------------------------------------------------------------
@@ -107,6 +113,10 @@
gPowerHalController.init();
}
+static jlong nativeGetHintSessionPreferredRate(JNIEnv* /* env */, jclass /* clazz */) {
+ return static_cast<jlong>(getHintSessionPreferredRate());
+}
+
static jlong nativeCreateHintSession(JNIEnv* env, jclass /* clazz */, jint tgid, jint uid,
jintArray tids, jlong durationNanos) {
ScopedIntArrayRO tidArray(env, tids);
@@ -165,14 +175,16 @@
setThreads(session_ptr, threadIds);
}
-static jlong nativeGetHintSessionPreferredRate(JNIEnv* /* env */, jclass /* clazz */) {
- return static_cast<jlong>(getHintSessionPreferredRate());
+static void nativeSetMode(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jint mode,
+ jboolean enabled) {
+ setMode(session_ptr, static_cast<SessionMode>(mode), enabled);
}
// ----------------------------------------------------------------------------
static const JNINativeMethod sHintManagerServiceMethods[] = {
/* name, signature, funcPtr */
{"nativeInit", "()V", (void*)nativeInit},
+ {"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate},
{"nativeCreateHintSession", "(II[IJ)J", (void*)nativeCreateHintSession},
{"nativePauseHintSession", "(J)V", (void*)nativePauseHintSession},
{"nativeResumeHintSession", "(J)V", (void*)nativeResumeHintSession},
@@ -181,7 +193,7 @@
{"nativeReportActualWorkDuration", "(J[J[J)V", (void*)nativeReportActualWorkDuration},
{"nativeSendHint", "(JI)V", (void*)nativeSendHint},
{"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
- {"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate},
+ {"nativeSetMode", "(JIZ)V", (void*)nativeSetMode},
};
int register_android_server_HintManagerService(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 522ee34..e7855bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -79,14 +79,7 @@
@Override
public boolean isScreenCaptureAllowed(int userHandle) {
- if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) {
- return isScreenCaptureAllowedInPolicyEngine(userHandle);
- } else {
- synchronized (mLock) {
- return mScreenCaptureDisallowedUser != UserHandle.USER_ALL
- && mScreenCaptureDisallowedUser != userHandle;
- }
- }
+ return isScreenCaptureAllowedInPolicyEngine(userHandle);
}
private boolean isScreenCaptureAllowedInPolicyEngine(int userHandle) {
@@ -182,11 +175,7 @@
synchronized (mLock) {
pw.println("Device policy cache:");
pw.increaseIndent();
- if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) {
- pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers);
- } else {
- pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser);
- }
+ pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers);
pw.println("Password quality: " + mPasswordQuality);
pw.println("Permission policy: " + mPermissionPolicy);
pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c323a7f..b3fa782 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,6 +241,7 @@
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import static android.provider.Telephony.Carriers.INVALID_APN_ID;
import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
+
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -1190,9 +1191,7 @@
maybeResumeDeviceWideLoggingLocked();
}
}
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handleUserRemoved(userHandle);
- }
+ mDevicePolicyEngine.handleUserRemoved(userHandle);
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
synchronized (getLockObject()) {
@@ -1442,10 +1441,7 @@
&& (owner.getPackageName().equals(packageName))) {
startOwnerService(userHandle, "package-broadcast");
}
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handlePackageChanged(
- packageName, userHandle, removedAdminPackage);
- }
+ mDevicePolicyEngine.handlePackageChanged(packageName, userHandle, removedAdminPackage);
// Persist updates if the removed package was an admin or delegate.
if (removedAdmin || removedDelegate) {
saveSettingsLocked(policy.mUserId);
@@ -1453,7 +1449,6 @@
}
if (removedAdmin) {
// The removed admin might have disabled camera, so update user restrictions.
- pushUserRestrictions(userHandle);
pushMeteredDisabledPackages(userHandle);
}
}
@@ -2144,9 +2139,7 @@
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
mDeviceManagementResourcesProvider.load();
- if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
- mDevicePolicyEngine.load();
- }
+ mDevicePolicyEngine.load();
mContactSystemRoleHolders = fetchOemSystemHolders(/* roleResIds...= */
com.android.internal.R.string.config_defaultSms,
@@ -2278,11 +2271,9 @@
if (parentAdmin != null) {
parentAdmin.userRestrictions = null;
}
- pushUserRestrictions(userHandle);
}
mOwners.removeProfileOwner(userHandle);
mOwners.writeProfileOwner(userHandle);
- pushScreenCapturePolicy(userHandle);
DevicePolicyData policy = mUserData.get(userHandle);
if (policy != null) {
@@ -2640,20 +2631,14 @@
ActiveAdmin profileOwner, boolean newOwner) {
if (newOwner || mInjector.settingsSecureGetIntForUser(
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.getPolicyDefinitionForUserRestriction(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- profileOwner.info.getComponent(),
- profileOwner.getUserHandle().getIdentifier()),
- new BooleanPolicyValue(true),
- userId);
- } else {
- profileOwner.ensureUserRestrictions().putBoolean(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
- saveUserRestrictionsLocked(userId);
- }
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ profileOwner.info.getComponent(),
+ profileOwner.getUserHandle().getIdentifier()),
+ new BooleanPolicyValue(true),
+ userId);
mInjector.settingsSecurePutIntForUser(
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
}
@@ -2668,41 +2653,18 @@
if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
return; // The same set of default restrictions has been already applied.
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
- for (String restriction : defaultRestrictions) {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- admin.info.getComponent(),
- admin.getUserHandle().getIdentifier()),
- new BooleanPolicyValue(true),
- userId);
- }
- admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
- Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " +
- defaultRestrictions);
- return;
+ for (String restriction : defaultRestrictions) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(),
+ admin.getUserHandle().getIdentifier()),
+ new BooleanPolicyValue(true),
+ userId);
}
-
- Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
-
- if (VERBOSE_LOG) {
- Slogf.d(LOG_TAG, "Default enabled restrictions: "
- + defaultRestrictions
- + ". Restrictions already enabled: "
- + admin.defaultEnabledRestrictionsAlreadySet);
- }
-
- final Set<String> restrictionsToSet = new ArraySet<>(defaultRestrictions);
- restrictionsToSet.removeAll(admin.defaultEnabledRestrictionsAlreadySet);
- if (!restrictionsToSet.isEmpty()) {
- for (final String restriction : restrictionsToSet) {
- admin.ensureUserRestrictions().putBoolean(restriction, true);
- }
- admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
- Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
- saveUserRestrictionsLocked(userId);
- }
+ admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
+ Slogf.i(LOG_TAG, "Enabled the following restrictions by default: "
+ + defaultRestrictions);
}
private void setDeviceOwnershipSystemPropertyLocked() {
@@ -2765,7 +2727,6 @@
// Apply user restriction to parent active admin instead
parent.ensureUserRestrictions().putBoolean(
UserManager.DISALLOW_CONFIG_DATE_TIME, true);
- pushUserRestrictions(userId);
}
}
}
@@ -3297,10 +3258,6 @@
policy.validatePasswordOwner();
updateMaximumTimeToLockLocked(userHandle);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
- updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
- }
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3470,9 +3427,6 @@
}
revertTransferOwnershipIfNecessaryLocked();
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
- }
}
// In case flag value has changed, we apply it during boot to avoid doing it concurrently
@@ -3545,9 +3499,6 @@
deleteTransferOwnershipBundleLocked(metadata.userId);
}
updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- pushUserControlDisabledPackagesLocked(metadata.userId);
- }
}
private void maybeLogStart() {
@@ -3584,13 +3535,6 @@
}
void handleStartUser(int userId) {
- synchronized (getLockObject()) {
- pushScreenCapturePolicy(userId);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- pushUserControlDisabledPackagesLocked(userId);
- }
- }
- pushUserRestrictions(userId);
// When system user is started (device boot), load cache for all users.
// This is to mitigate the potential race between loading the cache and keyguard
// reading the value during user switch, due to onStartUser() being asynchronous.
@@ -3615,9 +3559,7 @@
}
startOwnerService(userId, "start-user");
- if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
- mDevicePolicyEngine.handleStartUser(userId);
- }
+ mDevicePolicyEngine.handleStartUser(userId);
}
void pushUserControlDisabledPackagesLocked(int userId) {
@@ -3642,9 +3584,7 @@
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
- if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
- mDevicePolicyEngine.handleUnlockUser(userId);
- }
+ mDevicePolicyEngine.handleUnlockUser(userId);
}
void handleOnUserUnlocked(int userId) {
@@ -3654,9 +3594,7 @@
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
- if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
- mDevicePolicyEngine.handleStopUser(userId);
- }
+ mDevicePolicyEngine.handleStopUser(userId);
}
private void startOwnerService(int userId, String actionForLog) {
@@ -3690,9 +3628,7 @@
}
for (Integer userId : deletedUsers) {
removeUserData(userId);
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handleUserRemoved(userId);
- }
+ mDevicePolicyEngine.handleUserRemoved(userId);
}
}
@@ -3879,16 +3815,14 @@
final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
final int oldAdminUid = adminToTransfer.getUid();
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- EnforcingAdmin oldAdmin =
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- outgoingReceiver, userHandle, adminToTransfer);
- EnforcingAdmin newAdmin =
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- incomingReceiver, userHandle, adminToTransfer);
+ EnforcingAdmin oldAdmin =
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ outgoingReceiver, userHandle, adminToTransfer);
+ EnforcingAdmin newAdmin =
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ incomingReceiver, userHandle, adminToTransfer);
- mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin);
- }
+ mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin);
adminToTransfer.transfer(incomingDeviceInfo);
policy.mAdminMap.remove(outgoingReceiver);
@@ -4194,11 +4128,9 @@
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
}
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.removePoliciesForAdmin(
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- adminReceiver, userHandle, admin));
- }
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ adminReceiver, userHandle, admin));
}
private boolean canSetPasswordQualityOnParent(String packageName, final CallerIdentity caller) {
@@ -7558,47 +7490,17 @@
if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) {
return;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(callerPackageName);
- } else {
- caller = getCallerIdentity();
- }
- ActiveAdmin admin;
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
boolean calledByProfileOwnerOnOrgOwnedDevice =
isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId());
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
- /*admin=*/ null,
- /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR},
- USES_POLICY_WIPE_DATA,
- caller.getPackageName(),
- factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- if (calledOnParentInstance) {
- Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice,
- "Wiping the entire device can only be done by a profile owner on "
- + "organization-owned device.");
- }
- if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || calledByProfileOwnerOnOrgOwnedDevice
- || isFinancedDeviceOwner(caller),
- "Only device owners or profile owners of organization-owned device can set "
- + "WIPE_RESET_PROTECTION_DATA");
- }
- synchronized (getLockObject()) {
- admin = getActiveAdminWithPolicyForUidLocked(/* who= */ null,
- DeviceAdminInfo.USES_POLICY_WIPE_DATA, caller.getUid());
- }
- Preconditions.checkCallAuthorization(
- (admin != null) || hasCallingOrSelfPermission(permission.MASTER_CLEAR),
- "No active admin for user %d and caller %d does not hold MASTER_CLEAR "
- + "permission",
- caller.getUserId(), caller.getUid());
- }
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+ /*admin=*/ null,
+ /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR},
+ USES_POLICY_WIPE_DATA,
+ caller.getPackageName(),
+ factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
+ ActiveAdmin admin = enforcingAdmin.getActiveAdmin();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
@@ -8639,62 +8541,36 @@
return;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackage);
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- caller = getCallerIdentity(who);
- if (parent) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller));
+ CallerIdentity caller = getCallerIdentity(who, callerPackage);
+ int callerUserId = Binder.getCallingUserHandle().getIdentifier();
+ int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId;
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(),
+ targetUserId);
+ if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller))
+ || isDefaultDeviceOwner(caller)) {
+ if (disabled) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+ admin,
+ new BooleanPolicyValue(disabled));
} else {
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
- || isDefaultDeviceOwner(caller));
- }
- }
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int callerUserId = Binder.getCallingUserHandle().getIdentifier();
- int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId;
- EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(),
- targetUserId);
- if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller))
- || isDefaultDeviceOwner(caller)) {
- if (disabled) {
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED,
- admin,
- new BooleanPolicyValue(disabled));
- } else {
- mDevicePolicyEngine.removeGlobalPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED,
- admin);
- }
- } else {
- if (disabled) {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED,
- admin,
- new BooleanPolicyValue(disabled),
- callerUserId);
- } else {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED,
- admin,
- callerUserId);
- }
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+ admin);
}
} else {
- synchronized (getLockObject()) {
- ActiveAdmin ap = getParentOfAdminIfRequired(
- getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent);
- if (ap.disableScreenCapture != disabled) {
- ap.disableScreenCapture = disabled;
- saveSettingsLocked(caller.getUserId());
- pushScreenCapturePolicy(caller.getUserId());
- }
+ if (disabled) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+ admin,
+ new BooleanPolicyValue(disabled),
+ callerUserId);
+ } else {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+ admin,
+ callerUserId);
}
}
DevicePolicyEventLogger
@@ -8704,42 +8580,6 @@
.write();
}
- // Push the screen capture policy for a given userId. If screen capture is disabled by the
- // DO or COPE PO on the parent profile, then this takes precedence as screen capture will
- // be disabled device-wide.
- private void pushScreenCapturePolicy(int adminUserId) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- return;
- }
- // Update screen capture device-wide if disabled by the DO or COPE PO on the parent profile.
- // TODO(b/261999445): remove
- ActiveAdmin admin;
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
- mUserManagerInternal.getProfileParentId(adminUserId));
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
- UserHandle.USER_SYSTEM);
- }
- if (admin != null && admin.disableScreenCapture) {
- setScreenCaptureDisabled(UserHandle.USER_ALL);
- return;
- }
- // Otherwise, update screen capture only for the calling user.
- admin = getProfileOwnerAdminLocked(adminUserId);
- if (admin != null && admin.disableScreenCapture) {
- setScreenCaptureDisabled(adminUserId);
- return;
- }
- // If the admin is permission based, update only for the calling user.
- admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId);
- if (admin != null && admin.disableScreenCapture) {
- setScreenCaptureDisabled(adminUserId);
- return;
- }
- setScreenCaptureDisabled(UserHandle.USER_NULL);
- }
-
// Set the latest screen capture policy, overriding any existing ones.
// userHandle can be one of USER_ALL, USER_NULL or a concrete userId.
private void setScreenCaptureDisabled(int userHandle) {
@@ -8766,14 +8606,10 @@
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED,
- userHandle);
- return disallowed != null && disallowed;
- } else {
- return !mPolicyCache.isScreenCaptureAllowed(userHandle);
- }
+ Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+ userHandle);
+ return disallowed != null && disallowed;
}
private void updateScreenCaptureDisabled() {
@@ -8882,23 +8718,9 @@
Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
"Managed profile cannot set auto time required");
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName());
- setGlobalUserRestrictionInternal(
- admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required);
- } else {
- ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- if (admin.requireAutoTime != required) {
- admin.requireAutoTime = required;
- saveSettingsLocked(caller.getUserId());
- requireAutoTimeChanged = true;
- }
- // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so
- // propagate updated restrictions to the framework.
- if (requireAutoTimeChanged) {
- pushUserRestrictions(caller.getUserId());
- }
- }
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName());
+ setGlobalUserRestrictionInternal(
+ admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required);
}
// Turn AUTO_TIME on in settings if it is required
if (required) {
@@ -8921,31 +8743,11 @@
if (!mHasFeature) {
return false;
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Boolean required = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.getPolicyDefinitionForUserRestriction(
- UserManager.DISALLOW_CONFIG_DATE_TIME),
- mInjector.binderGetCallingUserHandle().getIdentifier());
- return required != null && required;
- } else {
- synchronized (getLockObject()) {
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner != null && deviceOwner.requireAutoTime) {
- // If the device owner enforces auto time, we don't need to check the PO's
- return true;
- }
-
- // Now check to see if any profile owner on any user enforces auto time
- for (Integer userId : mOwners.getProfileOwnerKeys()) {
- ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
- if (profileOwner != null && profileOwner.requireAutoTime) {
- return true;
- }
- }
-
- return false;
- }
- }
+ Boolean required = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_CONFIG_DATE_TIME),
+ mInjector.binderGetCallingUserHandle().getIdentifier());
+ return required != null && required;
}
/**
@@ -9240,47 +9042,23 @@
return;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userId = caller.getUserId();
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED);
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_CAMERA,
- caller.getPackageName(),
- getProfileParentUserIfRequested(userId, parent));
- try {
- setBackwardCompatibleUserRestriction(
- caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
- } catch (IllegalStateException e) {
- throw new IllegalStateException(
- "Please use addUserRestriction or addUserRestrictionGlobally using the key"
- + " UserManager.DISALLOW_CAMERA to disable the camera locally or"
- + " globally, respectively");
- }
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- if (parent) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
- synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
- if (admin.disableCamera != disabled) {
- admin.disableCamera = disabled;
- saveSettingsLocked(userId);
- }
- }
- // Tell the user manager that the restrictions have changed.
- pushUserRestrictions(userId);
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_CAMERA,
+ caller.getPackageName(),
+ getProfileParentUserIfRequested(userId, parent));
+ try {
+ setBackwardCompatibleUserRestriction(
+ caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(
+ "Please use addUserRestriction or addUserRestrictionGlobally using the key"
+ + " UserManager.DISALLOW_CAMERA to disable the camera locally or"
+ + " globally, respectively");
}
final int affectedUserId = parent ? getProfileParentId(userId) : userId;
@@ -9306,66 +9084,26 @@
if (!mHasFeature) {
return false;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasFullCrossUsersPermission(caller, userHandle)
- || isCameraServerUid(caller)
- || hasPermission(MANAGE_DEVICE_POLICY_CAMERA,
- caller.getPackageName(), userHandle)
- || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName()));
- } else {
- Preconditions.checkCallAuthorization(
- hasFullCrossUsersPermission(caller, userHandle) || isCameraServerUid(caller));
- if (parent) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()));
- }
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ Preconditions.checkCallAuthorization(
+ hasFullCrossUsersPermission(caller, userHandle)
+ || isCameraServerUid(caller)
+ || hasPermission(MANAGE_DEVICE_POLICY_CAMERA,
+ caller.getPackageName(), userHandle)
+ || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName()));
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- PolicyDefinition<Boolean> policy =
- PolicyDefinition.getPolicyDefinitionForUserRestriction(
- UserManager.DISALLOW_CAMERA);
- if (who != null) {
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName);
- return Boolean.TRUE.equals(
- mDevicePolicyEngine.getLocalPolicySetByAdmin(
- policy, admin, affectedUserId));
- } else {
- return Boolean.TRUE.equals(
- mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId));
- }
+ PolicyDefinition<Boolean> policy =
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_CAMERA);
+ if (who != null) {
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName);
+ return Boolean.TRUE.equals(
+ mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ policy, admin, affectedUserId));
} else {
- synchronized (getLockObject()) {
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return (admin != null) && admin.disableCamera;
- }
- // First, see if DO has set it. If so, it's device-wide.
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner != null && deviceOwner.disableCamera) {
- return true;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(affectedUserId);
-
- // Determine whether or not the device camera is disabled for any active admins.
- for (ActiveAdmin activeAdmin : admins) {
- if (activeAdmin.disableCamera) {
- return true;
- }
- }
- return false;
- }
+ return Boolean.TRUE.equals(
+ mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId));
}
}
@@ -10117,9 +9855,6 @@
clearUserPoliciesLocked(userId);
clearOverrideApnUnchecked();
clearApplicationRestrictions(userId);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- mInjector.getPackageManagerInternal().clearBlockUninstallForUser(userId);
- }
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
@@ -10131,16 +9866,11 @@
setNetworkLoggingActiveInternal(false);
deleteTransferOwnershipBundleLocked(userId);
toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- pushUserControlDisabledPackagesLocked(userId);
- }
setGlobalSettingDeviceOwnerType(DEVICE_OWNER_TYPE_DEFAULT);
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.removePoliciesForAdmin(
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- admin.info.getComponent(), userId, admin));
- }
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(), userId, admin));
}
private void clearApplicationRestrictions(int userId) {
@@ -10289,11 +10019,9 @@
applyProfileRestrictionsIfDeviceOwnerLocked();
setNetworkLoggingActiveInternal(false);
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.removePoliciesForAdmin(
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- admin.info.getComponent(), userId, admin));
- }
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(), userId, admin));
}
@Override
@@ -10337,9 +10065,6 @@
policy.mAffiliationIds.clear();
resetAffiliationCacheLocked();
policy.mLockTaskPackages.clear();
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
- }
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
saveSettingsLocked(userId);
@@ -10347,7 +10072,6 @@
mIPermissionManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userId);
- pushUserRestrictions(userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slogf.wtf(LOG_TAG, "Failing in updatePermissionFlagsForAllApps", re);
@@ -11434,53 +11158,30 @@
@Override
public void addPersistentPreferredActivity(ComponentName who, String callerPackageName,
IntentFilter filter, ComponentName activity) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userId = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin;
- if (who == null) {
- enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- caller.getPackageName(),
- userId);
- } else {
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
- || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
- enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
- }
- if (!isPackageInstalledForUser(activity.getPackageName(), userId)) {
- // Fail early as packageManager doesn't persist the activity if its not installed.
- return;
- }
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
- enforcingAdmin,
- new ComponentNamePolicyValue(activity),
+ EnforcingAdmin enforcingAdmin;
+ if (who == null) {
+ enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_LOCK_TASK,
+ caller.getPackageName(),
userId);
} else {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isProfileOwner(caller)
|| isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
- synchronized (getLockObject()) {
- long id = mInjector.binderClearCallingIdentity();
- try {
- mIPackageManager.addPersistentPreferredActivity(filter, activity, userId);
- mIPackageManager.flushPackageRestrictionsAsUser(userId);
- } catch (RemoteException re) {
- // Shouldn't happen
- Slog.wtf(LOG_TAG, "Error adding persistent preferred activity", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
- }
+ enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
}
+ if (!isPackageInstalledForUser(activity.getPackageName(), userId)) {
+ // Fail early as packageManager doesn't persist the activity if its not installed.
+ return;
+ }
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
+ enforcingAdmin,
+ new ComponentNamePolicyValue(activity),
+ userId);
final String activityPackage =
(activity != null ? activity.getPackageName() : null);
DevicePolicyEventLogger
@@ -11493,51 +11194,25 @@
@Override
public void clearPackagePersistentPreferredActivities(ComponentName who,
String callerPackageName, String packageName) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userId = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin;
- if (who == null) {
- enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- caller.getPackageName(),
- userId);
- } else {
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
- || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
- enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
- }
- clearPackagePersistentPreferredActivitiesFromPolicyEngine(
- enforcingAdmin,
- packageName,
+ EnforcingAdmin enforcingAdmin;
+ if (who == null) {
+ enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_LOCK_TASK,
+ caller.getPackageName(),
userId);
} else {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isProfileOwner(caller)
|| isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
- synchronized (getLockObject()) {
- long id = mInjector.binderClearCallingIdentity();
- try {
- mIPackageManager.clearPackagePersistentPreferredActivities(packageName,
- userId);
- mIPackageManager.flushPackageRestrictionsAsUser(userId);
- } catch (RemoteException re) {
- // Shouldn't happen
- Slogf.wtf(
- LOG_TAG, "Error when clearing package persistent preferred activities",
- re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
- }
+ enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
}
+ clearPackagePersistentPreferredActivitiesFromPolicyEngine(
+ enforcingAdmin,
+ packageName,
+ userId);
}
/**
@@ -12274,28 +11949,15 @@
return false;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
int userId = getProfileParentUserIfRequested(
caller.getUserId(), calledOnParentInstance);
if (calledOnParentInstance) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
Preconditions.checkArgument(packageList == null || packageList.isEmpty(),
"Permitted input methods must allow all input methods or only "
+ "system input methods when called on the parent instance of an "
+ "organization-owned device");
- } else if (!isPolicyEngineForFinanceFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
if (packageList != null) {
@@ -12320,28 +11982,20 @@
}
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
- caller.getPackageName(), userId);
- if (packageList == null) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.PERMITTED_INPUT_METHODS,
- admin,
- userId);
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMITTED_INPUT_METHODS,
- admin,
- new StringSetPolicyValue(new HashSet<>(packageList)),
- userId);
- }
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
+ caller.getPackageName(), userId);
+ if (packageList == null) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS,
+ admin,
+ userId);
} else {
- ActiveAdmin admin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()),
- calledOnParentInstance);
- admin.permittedInputMethods = packageList;
- saveSettingsLocked(caller.getUserId());
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS,
+ admin,
+ new StringSetPolicyValue(new HashSet<>(packageList)),
+ userId);
}
}
@@ -12371,37 +12025,14 @@
return null;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- }
-
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- if (calledOnParentInstance) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller));
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- }
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int affectedUser = calledOnParentInstance ? getProfileParentId(
- caller.getUserId()) : caller.getUserId();
- Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
- return policy == null ? null : new ArrayList<>(policy);
- } else {
- ActiveAdmin admin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(
- caller.getUserId()), calledOnParentInstance);
- return admin.permittedInputMethods;
- }
+ int affectedUser = calledOnParentInstance ? getProfileParentId(
+ caller.getUserId()) : caller.getUserId();
+ Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+ return policy == null ? null : new ArrayList<>(policy);
}
}
@@ -12419,29 +12050,9 @@
}
private @Nullable List<String> getPermittedInputMethodsUnchecked(@UserIdInt int userId) {
- List<String> result = null;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.PERMITTED_INPUT_METHODS, userId);
- result = policy == null ? null : new ArrayList<>(policy);
- } else {
- synchronized (getLockObject()) {
- // Only device or profile owners can have permitted lists set.
- List<ActiveAdmin> admins =
- getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
- userId);
- for (ActiveAdmin admin : admins) {
- List<String> fromAdmin = admin.permittedInputMethods;
- if (fromAdmin != null) {
- if (result == null) {
- result = new ArrayList<String>(fromAdmin);
- } else {
- result.retainAll(fromAdmin);
- }
- }
- }
- }
- }
+ Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, userId);
+ List<String> result = policy == null ? null : new ArrayList<>(policy);
// If we have a permitted list add all system input methods.
if (result != null) {
@@ -12472,39 +12083,23 @@
String.format(NOT_SYSTEM_CALLER_MSG,
"query if an input method is disabled by admin"));
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle;
- Map<EnforcingAdmin, PolicyValue<Set<String>>> policies =
- mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
- PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
- EnforcingAdmin admin = null;
- for (EnforcingAdmin a : policies.keySet()) {
- if (a.getPackageName().equals(who.getPackageName())) {
- if (policies.get(a).getValue() == null) {
- return true;
- } else {
- return checkPackagesInPermittedListOrSystem(
- Collections.singletonList(packageName),
- new ArrayList<>(policies.get(a).getValue()), affectedUser);
- }
- }
- }
- // Admin didn't set a policy
- return false;
- } else {
- synchronized (getLockObject()) {
- ActiveAdmin admin = getParentOfAdminIfRequired(
- getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
- if (admin == null) {
- return false;
- }
- if (admin.permittedInputMethods == null) {
+ int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle;
+ Map<EnforcingAdmin, PolicyValue<Set<String>>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+ for (EnforcingAdmin a : policies.keySet()) {
+ if (a.getPackageName().equals(who.getPackageName())) {
+ if (policies.get(a).getValue() == null) {
return true;
+ } else {
+ return checkPackagesInPermittedListOrSystem(
+ Collections.singletonList(packageName),
+ new ArrayList<>(policies.get(a).getValue()), affectedUser);
}
- return checkPackagesInPermittedListOrSystem(Collections.singletonList(packageName),
- admin.permittedInputMethods, userHandle);
}
}
+ // Admin didn't set a policy
+ return false;
}
@Override
@@ -12775,12 +12370,9 @@
+ ", token=" + token);
}
+ mDevicePolicyEngine.handleUserCreated(user);
+
final int userId = user.id;
-
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handleUserCreated(user);
- }
-
if (token != null) {
synchronized (getLockObject()) {
if (mPendingUserCreatedCallbackTokens.contains(token)) {
@@ -13374,79 +12966,54 @@
ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
boolean parent) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackage);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackage);
int userId = caller.getUserId();
int affectedUserId = parent ? getProfileParentId(userId) : userId;
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- if (!isDeviceOwner(caller) && !isProfileOwner(caller)) {
- EnforcingAdmin admin = enforcePermissionForUserRestriction(
- who,
- key,
- caller.getPackageName(),
- affectedUserId);
- if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
- throw new IllegalStateException("Calling package is not targeting Android U.");
- }
- if (!UserRestrictionsUtils.isValidRestriction(key)) {
- throw new IllegalArgumentException("Invalid restriction key: " + key);
- }
- PolicyDefinition<Boolean> policyDefinition =
- PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
- if (enabledFromThisOwner) {
- setLocalUserRestrictionInternal(
- admin, key, /* enabled= */ true, affectedUserId);
- } else {
- // Remove any local and global policy that was set by the admin
- if (!policyDefinition.isLocalOnlyPolicy()) {
- setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false);
- }
- if (!policyDefinition.isGlobalOnlyPolicy()) {
- setLocalUserRestrictionInternal(admin, key, /* enabled= */ false,
- userId);
-
- int parentUserId = getProfileParentId(userId);
- if (parentUserId != userId) {
- setLocalUserRestrictionInternal(
- admin, key, /* enabled= */ false, parentUserId);
- }
- }
- }
+ if (!isDeviceOwner(caller) && !isProfileOwner(caller)) {
+ EnforcingAdmin admin = enforcePermissionForUserRestriction(
+ who,
+ key,
+ caller.getPackageName(),
+ affectedUserId);
+ if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
+ throw new IllegalStateException("Calling package is not targeting Android U.");
+ }
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ throw new IllegalArgumentException("Invalid restriction key: " + key);
+ }
+ PolicyDefinition<Boolean> policyDefinition =
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
+ if (enabledFromThisOwner) {
+ setLocalUserRestrictionInternal(
+ admin, key, /* enabled= */ true, affectedUserId);
} else {
- if (!UserRestrictionsUtils.isValidRestriction(key)) {
- return;
+ // Remove any local and global policy that was set by the admin
+ if (!policyDefinition.isLocalOnlyPolicy()) {
+ setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false);
}
- Objects.requireNonNull(who, "ComponentName is null");
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
- checkAdminCanSetRestriction(caller, parent, key);
- setBackwardCompatibleUserRestriction(
- caller, admin, key, enabledFromThisOwner, parent);
+ if (!policyDefinition.isGlobalOnlyPolicy()) {
+ setLocalUserRestrictionInternal(admin, key, /* enabled= */ false,
+ userId);
+
+ int parentUserId = getProfileParentId(userId);
+ if (parentUserId != userId) {
+ setLocalUserRestrictionInternal(
+ admin, key, /* enabled= */ false, parentUserId);
+ }
+ }
}
} else {
if (!UserRestrictionsUtils.isValidRestriction(key)) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
checkAdminCanSetRestriction(caller, parent, key);
- synchronized (getLockObject()) {
- final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(userId), parent);
- // Save the restriction to ActiveAdmin.
- final Bundle restrictions = activeAdmin.ensureUserRestrictions();
- if (enabledFromThisOwner) {
- restrictions.putBoolean(key, true);
- } else {
- restrictions.remove(key);
- }
- saveUserRestrictionsLocked(userId);
- }
+ setBackwardCompatibleUserRestriction(
+ caller, admin, key, enabledFromThisOwner, parent);
}
logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
}
@@ -13532,9 +13099,6 @@
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- throw new IllegalStateException("Feature flag is not enabled.");
- }
if (isDeviceOwner(caller) || isProfileOwner(caller)) {
throw new SecurityException("Admins are not allowed to call this API.");
}
@@ -13604,121 +13168,33 @@
key, enabled, caller.toString());
}
- private void saveUserRestrictionsLocked(int userId) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- // User restrictions are handled in the policy engine
- return;
- }
- saveSettingsLocked(userId);
- pushUserRestrictions(userId);
- sendChangedNotification(userId);
- }
-
- /**
- * Pushes the user restrictions originating from a specific user.
- *
- * If called by the profile owner of an organization-owned device, the global and local
- * user restrictions will be an accumulation of the global user restrictions from the profile
- * owner active admin and its parent active admin. The key of the local user restrictions set
- * will be the target user id.
- */
- private void pushUserRestrictions(int originatingUserId) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- // User restrictions are handled in the policy engine
- return;
- }
- final Bundle global;
- final RestrictionsSet local = new RestrictionsSet();
- final boolean isDeviceOwner;
- synchronized (getLockObject()) {
- isDeviceOwner = mOwners.isDeviceOwnerUserId(originatingUserId);
- if (isDeviceOwner) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner == null) {
- return; // Shouldn't happen.
- }
- global = deviceOwner.getGlobalUserRestrictions(OWNER_TYPE_DEVICE_OWNER);
- local.updateRestrictions(originatingUserId, deviceOwner.getLocalUserRestrictions(
- OWNER_TYPE_DEVICE_OWNER));
- } else {
- final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(originatingUserId);
- if (profileOwner == null) {
- return;
- }
- global = profileOwner.getGlobalUserRestrictions(OWNER_TYPE_PROFILE_OWNER);
- local.updateRestrictions(originatingUserId, profileOwner.getLocalUserRestrictions(
- OWNER_TYPE_PROFILE_OWNER));
- // Global (device-wide) and local user restrictions set by the profile owner of an
- // organization-owned device are stored in the parent ActiveAdmin instance.
- if (isProfileOwnerOfOrganizationOwnedDevice(
- profileOwner.getUserHandle().getIdentifier())) {
- // The global restrictions set on the parent ActiveAdmin instance need to be
- // merged with the global restrictions set on the profile owner ActiveAdmin
- // instance, since both are to be applied device-wide.
- UserRestrictionsUtils.merge(global,
- profileOwner.getParentActiveAdmin().getGlobalUserRestrictions(
- OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
- // The local restrictions set on the parent ActiveAdmin instance are only to be
- // applied to the primary user. They therefore need to be added the local
- // restriction set with the primary user id as the key, in this case the
- // primary user id is the target user.
- local.updateRestrictions(
- getProfileParentId(profileOwner.getUserHandle().getIdentifier()),
- profileOwner.getParentActiveAdmin().getLocalUserRestrictions(
- OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
- }
- }
- }
- mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId, global, local,
- isDeviceOwner);
- }
-
@Override
public Bundle getUserRestrictions(ComponentName who, String callerPackage, boolean parent) {
if (!mHasFeature) {
return null;
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackage);
- } else {
- caller = getCallerIdentity(who);
- }
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int targetUserId = parent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
- if (isDeviceOwner(caller) || isProfileOwner(caller)) {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isFinancedDeviceOwner(caller)
- || isProfileOwner(caller)
- || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
-
- Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
- // Add global restrictions set by the admin as well.
- restrictions.putAll(
- getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL));
- return restrictions;
- } else {
- if (!mInjector.isChangeEnabled(
- ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
- throw new IllegalStateException("Calling package is not targeting Android U.");
- }
- return getUserRestrictionsFromPolicyEngine(admin, targetUserId);
- }
- } else {
+ CallerIdentity caller = getCallerIdentity(who, callerPackage);
+ int targetUserId = parent
+ ? getProfileParentId(caller.getUserId()) : caller.getUserId();
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
|| isFinancedDeviceOwner(caller)
|| isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
- synchronized (getLockObject()) {
- final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
- return activeAdmin.userRestrictions;
+
+ Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
+ // Add global restrictions set by the admin as well.
+ restrictions.putAll(
+ getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL));
+ return restrictions;
+ } else {
+ if (!mInjector.isChangeEnabled(
+ ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
+ throw new IllegalStateException("Calling package is not targeting Android U.");
}
+ return getUserRestrictionsFromPolicyEngine(admin, targetUserId);
}
}
@@ -13889,10 +13365,6 @@
return null;
}
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- throw new IllegalStateException("Feature flag is not enabled.");
- }
-
EnforcingAdmin admin = getEnforcingAdminForCaller(/*who=*/ null, caller.getPackageName());
return getUserRestrictionsFromPolicyEngine(admin,
@@ -13922,13 +13394,7 @@
boolean hidden, boolean parent) {
CallerIdentity caller = getCallerIdentity(who, callerPackage);
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
- }
+ enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
List<String> exemptApps = listPolicyExemptAppsUnchecked(mContext);
if (exemptApps.contains(packageName)) {
@@ -13940,11 +13406,6 @@
boolean result;
synchronized (getLockObject()) {
if (parent) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(
- caller.getUserId()) && isManagedProfile(caller.getUserId()));
- }
// Ensure the package provided is a system package, this is to ensure that this
// API cannot be used to leak if certain non-system package exists in the person
// profile.
@@ -13957,29 +13418,24 @@
Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
packageName, hidden, userId);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.APPLICATION_HIDDEN(packageName),
- admin,
- new BooleanPolicyValue(hidden),
- userId);
- result = mInjector.binderWithCleanCallingIdentity(() -> {
- try {
- // This is a best effort to continue returning the same value that was
- // returned before the policy engine migration.
- return mInjector.getIPackageManager().getPackageInfo(
- packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null
- && (mIPackageManager.getApplicationHiddenSettingAsUser(
- packageName, userId) == hidden);
- } catch (RemoteException e) {
- return false;
- }
- });
- } else {
- result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
- .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
- }
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.APPLICATION_HIDDEN(packageName),
+ admin,
+ new BooleanPolicyValue(hidden),
+ userId);
+ result = mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ // This is a best effort to continue returning the same value that was
+ // returned before the policy engine migration.
+ return mInjector.getIPackageManager().getPackageInfo(
+ packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null
+ && (mIPackageManager.getApplicationHiddenSettingAsUser(
+ packageName, userId) == hidden);
+ } catch (RemoteException e) {
+ return false;
+ }
+ });
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
@@ -13996,23 +13452,11 @@
String packageName, boolean parent) {
CallerIdentity caller = getCallerIdentity(who, callerPackage);
int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- // TODO: Also support DELEGATION_PACKAGE_ACCESS
- enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && isCallerDelegate(
- caller, DELEGATION_PACKAGE_ACCESS)));
- }
+ // TODO: Also support DELEGATION_PACKAGE_ACCESS
+ enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
synchronized (getLockObject()) {
if (parent) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())
- && isManagedProfile(caller.getUserId()));
- }
// Ensure the package provided is a system package.
mInjector.binderWithCleanCallingIdentity(() ->
enforcePackageIsSystemPackage(packageName, userId));
@@ -14199,57 +13643,26 @@
enforceMaxStringLength(accountType, "account type");
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int affectedUser = getAffectedUser(parent);
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- caller.getPackageName(),
- affectedUser
- );
- if (disabled) {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
- enforcingAdmin,
- new BooleanPolicyValue(disabled),
- affectedUser);
- } else {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
- enforcingAdmin,
- affectedUser);
- }
+ int affectedUser = getAffectedUser(parent);
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+ caller.getPackageName(),
+ affectedUser
+ );
+ if (disabled) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+ enforcingAdmin,
+ new BooleanPolicyValue(disabled),
+ affectedUser);
} else {
- final ActiveAdmin ap;
- Objects.requireNonNull(who, "ComponentName is null");
- /*
- * When called on the parent DPM instance (parent == true), affects active admin
- * selection in two ways:
- * * The ActiveAdmin must be of an org-owned profile owner.
- * * The parent ActiveAdmin instance should be used for managing the restriction.
- */
- if (parent) {
- ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller),
- parent);
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- ap = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
- }
- if (disabled) {
- ap.accountTypesWithManagementDisabled.add(accountType);
- } else {
- ap.accountTypesWithManagementDisabled.remove(accountType);
- }
- saveSettingsLocked(UserHandle.getCallingUserId());
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+ enforcingAdmin,
+ affectedUser);
}
}
}
@@ -14266,62 +13679,35 @@
if (!mHasFeature) {
return null;
}
- CallerIdentity caller;
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
final ArraySet<String> resultSet = new ArraySet<>();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- int affectedUser = parent ? getProfileParentId(userId) : userId;
- caller = getCallerIdentity(callerPackageName);
- if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- callerPackageName, affectedUser)
- && !hasFullCrossUsersPermission(caller, userId)) {
- throw new SecurityException("Caller does not have permission to call this on user: "
- + affectedUser);
+ int affectedUser = parent ? getProfileParentId(userId) : userId;
+ if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+ callerPackageName, affectedUser)
+ && !hasFullCrossUsersPermission(caller, userId)) {
+ throw new SecurityException("Caller does not have permission to call this on user: "
+ + affectedUser);
+ }
+ Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
+ PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+ affectedUser);
+
+ for (PolicyKey key : keys) {
+ if (!(key instanceof AccountTypePolicyKey)) {
+ throw new IllegalStateException("PolicyKey for "
+ + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
+ + "AccountTypePolicyKey");
}
- Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
- PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+ AccountTypePolicyKey parsedKey =
+ (AccountTypePolicyKey) key;
+ String accountType = Objects.requireNonNull(parsedKey.getAccountType());
+
+ Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
affectedUser);
-
- for (PolicyKey key : keys) {
- if (!(key instanceof AccountTypePolicyKey)) {
- throw new IllegalStateException("PolicyKey for "
- + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
- + "AccountTypePolicyKey");
- }
- AccountTypePolicyKey parsedKey =
- (AccountTypePolicyKey) key;
- String accountType = Objects.requireNonNull(parsedKey.getAccountType());
-
- Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
- affectedUser);
- if (disabled != null && disabled) {
- resultSet.add(accountType);
- }
- }
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
-
- synchronized (getLockObject()) {
- if (!parent) {
- final DevicePolicyData policy = getUserData(userId);
- for (ActiveAdmin admin : policy.mAdminList) {
- resultSet.addAll(admin.accountTypesWithManagementDisabled);
- }
- }
-
- // Check if there's a profile owner of an org-owned device and the method is called
- // for the parent user of this profile owner.
- final ActiveAdmin orgOwnedAdmin =
- getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
- final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
- || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
- if (shouldGetParentAccounts) {
- resultSet.addAll(
- orgOwnedAdmin.getParentActiveAdmin()
- .accountTypesWithManagementDisabled);
- }
+ if (disabled != null && disabled) {
+ resultSet.add(accountType);
}
}
return resultSet.toArray(new String[resultSet.size()]);
@@ -14332,46 +13718,19 @@
boolean uninstallBlocked) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
- who,
- new String[]{
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL
- },
- caller.getPackageName(),
- caller.getUserId());
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName),
- enforcingAdmin,
- new BooleanPolicyValue(uninstallBlocked),
- caller.getUserId());
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
- || isFinancedDeviceOwner(caller)))
- || (caller.hasPackage()
- && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
- final int userId = caller.getUserId();
- synchronized (getLockObject()) {
- long id = mInjector.binderClearCallingIdentity();
- try {
- mIPackageManager.setBlockUninstallForUser(
- packageName, uninstallBlocked, userId);
- } catch (RemoteException re) {
- // Shouldn't happen.
- Slogf.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
- }
- if (uninstallBlocked) {
- final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
- pmi.removeNonSystemPackageSuspensions(packageName, userId);
- pmi.removeDistractingPackageRestrictions(packageName, userId);
- pmi.flushPackageRestrictions(userId);
- }
- }
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+ who,
+ new String[]{
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL
+ },
+ caller.getPackageName(),
+ caller.getUserId());
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName),
+ enforcingAdmin,
+ new BooleanPolicyValue(uninstallBlocked),
+ caller.getUserId());
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED)
@@ -14898,49 +14257,35 @@
enforceMaxPackageNameLength(pkg);
}
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin;
- synchronized (getLockObject()) {
- enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
- }
- LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ EnforcingAdmin enforcingAdmin;
+ synchronized (getLockObject()) {
+ enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
+ }
+ LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ PolicyDefinition.LOCK_TASK,
+ enforcingAdmin,
+ caller.getUserId());
+ LockTaskPolicy policy;
+ if (currentPolicy == null) {
+ policy = new LockTaskPolicy(Set.of(packages));
+ } else {
+ policy = new LockTaskPolicy(currentPolicy);
+ policy.setPackages(Set.of(packages));
+ }
+ if (policy.getPackages().isEmpty()) {
+ mDevicePolicyEngine.removeLocalPolicy(
PolicyDefinition.LOCK_TASK,
enforcingAdmin,
caller.getUserId());
- LockTaskPolicy policy;
- if (currentPolicy == null) {
- policy = new LockTaskPolicy(Set.of(packages));
- } else {
- policy = new LockTaskPolicy(currentPolicy);
- policy.setPackages(Set.of(packages));
- }
- if (policy.getPackages().isEmpty()) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.LOCK_TASK,
- enforcingAdmin,
- caller.getUserId());
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.LOCK_TASK,
- enforcingAdmin,
- policy,
- caller.getUserId());
- }
} else {
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- enforceCanCallLockTaskLocked(caller);
- final int userHandle = caller.getUserId();
- setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
- }
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.LOCK_TASK,
+ enforcingAdmin,
+ policy,
+ caller.getUserId());
}
}
@@ -14955,32 +14300,18 @@
@Override
public String[] getLockTaskPackages(ComponentName who, String callerPackageName) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userHandle = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- synchronized (getLockObject()) {
- enforceCanQueryLockTaskLocked(who, caller.getPackageName());
- }
- LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.LOCK_TASK, userHandle);
- if (policy == null) {
- return new String[0];
- } else {
- return policy.getPackages().toArray(new String[policy.getPackages().size()]);
- }
+ synchronized (getLockObject()) {
+ enforceCanQueryLockTaskLocked(who, caller.getPackageName());
+ }
+ LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.LOCK_TASK, userHandle);
+ if (policy == null) {
+ return new String[0];
} else {
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- enforceCanCallLockTaskLocked(caller);
- final List<String> packages = getUserData(userHandle).mLockTaskPackages;
- return packages.toArray(new String[packages.size()]);
- }
+ return policy.getPackages().toArray(new String[policy.getPackages().size()]);
}
}
@@ -14996,18 +14327,12 @@
}
final int userId = mInjector.userHandleGetCallingUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.LOCK_TASK, userId);
- if (policy == null) {
- return false;
- }
- return policy.getPackages().contains(pkg);
- } else {
- synchronized (getLockObject()) {
- return getUserData(userId).mLockTaskPackages.contains(pkg);
- }
+ LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.LOCK_TASK, userId);
+ if (policy == null) {
+ return false;
}
+ return policy.getPackages().contains(pkg);
}
@Override
@@ -15021,54 +14346,40 @@
Preconditions.checkArgument(hasHome || !hasNotification,
"Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userHandle = caller.getUserId();
synchronized (getLockObject()) {
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin;
- synchronized (getLockObject()) {
- enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
- enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
- }
- LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ EnforcingAdmin enforcingAdmin;
+ synchronized (getLockObject()) {
+ enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
+ enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
+ }
+ LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ PolicyDefinition.LOCK_TASK,
+ enforcingAdmin,
+ caller.getUserId());
+ LockTaskPolicy policy;
+ if (currentPolicy == null) {
+ policy = new LockTaskPolicy(flags);
+ } else {
+ policy = new LockTaskPolicy(currentPolicy);
+ policy.setFlags(flags);
+ }
+ if (policy.getPackages().isEmpty()
+ && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
+ mDevicePolicyEngine.removeLocalPolicy(
PolicyDefinition.LOCK_TASK,
enforcingAdmin,
caller.getUserId());
- LockTaskPolicy policy;
- if (currentPolicy == null) {
- policy = new LockTaskPolicy(flags);
- } else {
- policy = new LockTaskPolicy(currentPolicy);
- policy.setFlags(flags);
- }
- if (policy.getPackages().isEmpty()
- && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.LOCK_TASK,
- enforcingAdmin,
- caller.getUserId());
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.LOCK_TASK,
- enforcingAdmin,
- policy,
- caller.getUserId());
- }
} else {
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- enforceCanCallLockTaskLocked(caller);
- enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
- setLockTaskFeaturesLocked(userHandle, flags);
- }
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.LOCK_TASK,
+ enforcingAdmin,
+ policy,
+ caller.getUserId());
}
}
@@ -15081,33 +14392,20 @@
@Override
public int getLockTaskFeatures(ComponentName who, String callerPackageName) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userHandle = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
- synchronized (getLockObject()) {
- enforceCanQueryLockTaskLocked(who, caller.getPackageName());
- }
- LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.LOCK_TASK, userHandle);
- if (policy == null) {
- // We default on the power button menu, in order to be consistent with pre-P
- // behaviour.
- return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
- }
- return policy.getFlags();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- enforceCanCallLockTaskLocked(caller);
- return getUserData(userHandle).mLockTaskFeatures;
- }
+ synchronized (getLockObject()) {
+ enforceCanQueryLockTaskLocked(who, caller.getPackageName());
}
+ LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.LOCK_TASK, userHandle);
+ if (policy == null) {
+ // We default on the power button menu, in order to be consistent with pre-P
+ // behaviour.
+ return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+ }
+ return policy.getFlags();
}
private void maybeClearLockTaskPolicyLocked() {
@@ -15118,34 +14416,14 @@
if (canDPCManagedUserUseLockTaskLocked(userId)) {
continue;
}
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies =
- mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
- PolicyDefinition.LOCK_TASK, userId);
- Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet());
- for (EnforcingAdmin admin : admins) {
- if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.LOCK_TASK, admin, userId);
- }
- }
- } else {
- final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
- // TODO(b/278438525): handle in the policy engine
- if (!lockTaskPackages.isEmpty()) {
- Slogf.d(LOG_TAG,
- "User id " + userId
- + " not affiliated. Clearing lock task packages");
- setLockTaskPackagesLocked(userId, Collections.<String>emptyList());
- }
- final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures;
- if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
- Slogf.d(LOG_TAG,
- "User id " + userId
- + " not affiliated. Clearing lock task features");
- setLockTaskFeaturesLocked(userId,
- DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+ Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.LOCK_TASK, userId);
+ Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet());
+ for (EnforcingAdmin admin : admins) {
+ if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.LOCK_TASK, admin, userId);
}
}
}
@@ -16442,69 +15720,22 @@
return result;
}
} else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- Boolean value = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId);
- if (value != null && value) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, userId);
- return result;
- }
- } else {
- synchronized (getLockObject()) {
- final DevicePolicyData policy = getUserData(userId);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- final ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.disableScreenCapture) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, userId);
- result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- admin.info.getComponent());
- return result;
- }
- }
- }
+ Boolean value = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId);
+ if (value != null && value) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ return result;
}
} else if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- PolicyDefinition<Boolean> policyDefinition =
- PolicyDefinition.getPolicyDefinitionForUserRestriction(
- UserManager.DISALLOW_CAMERA);
- Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
- if (value != null && value) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, userId);
- return result;
- }
- } else {
- synchronized (getLockObject()) {
- final DevicePolicyData policy = getUserData(userId);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- final ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.disableCamera) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, userId);
- result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- admin.info.getComponent());
- return result;
- }
- }
- // For the camera, a device owner on a different user can disable it globally,
- // so we need an additional check.
- if (result == null
- && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
- final ActiveAdmin admin = getDeviceOwnerAdminLocked();
- if (admin != null && admin.disableCamera) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, mOwners.getDeviceOwnerUserId());
- result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- admin.info.getComponent());
- return result;
- }
- }
- }
+ PolicyDefinition<Boolean> policyDefinition =
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(
+ UserManager.DISALLOW_CAMERA);
+ Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+ if (value != null && value) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ return result;
}
} else {
long ident = mInjector.binderClearCallingIdentity();
@@ -18564,14 +17795,9 @@
Slogf.d(LOG_TAG, "Current state of DevicePolicyData#mRemovingAdmins for user "
+ userHandle + ": " + policy.mRemovingAdmins);
- pushScreenCapturePolicy(userHandle);
-
Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
}
pushMeteredDisabledPackages(userHandle);
- // The removed admin might have disabled camera, so update user
- // restrictions.
- pushUserRestrictions(userHandle);
}
@Override
@@ -20483,20 +19709,13 @@
}
private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException {
- int lockTaskFeatures = 0;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId());
- lockTaskFeatures = policy == null
- // We default on the power button menu, in order to be consistent with pre-P
- // behaviour.
- ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
- : policy.getFlags();
- } else {
- //TODO(b/175285301): Explicitly get the user's identity to check.
- lockTaskFeatures =
- getUserData(getCurrentForegroundUserId()).mLockTaskFeatures;
- }
+ LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId());
+ int lockTaskFeatures = policy == null
+ // We default on the power button menu, in order to be consistent with pre-P
+ // behaviour.
+ ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+ : policy.getFlags();
return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
}
@@ -20680,41 +19899,22 @@
public void setUserControlDisabledPackages(ComponentName who, String callerPackageName,
List<String> packages) {
Objects.requireNonNull(packages, "packages is null");
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- caller.getPackageName(),
- caller.getUserId());
- Binder.withCleanCallingIdentity(() -> {
- if (packages.isEmpty()) {
- removeUserControlDisabledPackages(caller, enforcingAdmin);
- } else {
- addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages));
- }
- });
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
- synchronized (getLockObject()) {
- ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
- if (!Objects.equals(admin.protectedPackages, packages)) {
- admin.protectedPackages = packages.isEmpty() ? null : packages;
- saveSettingsLocked(caller.getUserId());
- pushUserControlDisabledPackagesLocked(caller.getUserId());
- }
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ caller.getPackageName(),
+ caller.getUserId());
+ Binder.withCleanCallingIdentity(() -> {
+ if (packages.isEmpty()) {
+ removeUserControlDisabledPackages(caller, enforcingAdmin);
+ } else {
+ addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages));
}
- }
+ });
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
@@ -20756,34 +19956,17 @@
@Override
public List<String> getUserControlDisabledPackages(ComponentName who,
String callerPackageName) {
- CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
-
- if (isPolicyEngineForFinanceFlagEnabled()) {
- enforceCanQuery(
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- caller.getPackageName(),
- caller.getUserId());
- // This retrieves the policy for the calling user only, DOs for example can't know
- // what's enforced globally or on another user.
- Set<String> packages = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
- caller.getUserId());
- return packages == null ? Collections.emptyList() : packages.stream().toList();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
- synchronized (getLockObject()) {
- ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
- return admin.protectedPackages != null
- ? admin.protectedPackages : Collections.emptyList();
- }
- }
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ enforceCanQuery(
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ caller.getPackageName(),
+ caller.getUserId());
+ // This retrieves the policy for the calling user only, DOs for example can't know
+ // what's enforced globally or on another user.
+ Set<String> packages = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
+ caller.getUserId());
+ return packages == null ? Collections.emptyList() : packages.stream().toList();
}
@Override
@@ -21066,27 +20249,18 @@
Slogf.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
parentUserId);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- // TODO(b/280602237): migrate properly
- ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
- if (profileOwner != null) {
- EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
- profileOwner.info.getComponent(),
- profileUserId,
- profileOwner);
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERSONAL_APPS_SUSPENDED,
- admin,
- new BooleanPolicyValue(suspended),
- parentUserId);
- }
- } else {
- if (suspended) {
- suspendPersonalAppsInPackageManager(parentUserId);
- } else {
- mInjector.getPackageManagerInternal().unsuspendForSuspendingPackage(
- PLATFORM_PACKAGE_NAME, parentUserId);
- }
+ // TODO(b/280602237): migrate properly
+ ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
+ if (profileOwner != null) {
+ EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ profileOwner.info.getComponent(),
+ profileUserId,
+ profileOwner);
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERSONAL_APPS_SUSPENDED,
+ admin,
+ new BooleanPolicyValue(suspended),
+ parentUserId);
}
synchronized (getLockObject()) {
@@ -22373,35 +21547,18 @@
public void setUsbDataSignalingEnabled(String packageName, boolean enabled) {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- 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.");
- }
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
- 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());
- }
- }
+ 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));
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22423,24 +21580,10 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- if (isPolicyEngineForFinanceFlagEnabled()) {
- 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();
- }
- }
- }
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -22849,9 +21992,6 @@
}
private void handleFinancedDeviceKioskRoleChange() {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
- return;
- }
Slog.i(LOG_TAG, "Handling action " + ACTION_DEVICE_FINANCING_STATE_CHANGED);
Intent intent = new Intent(ACTION_DEVICE_FINANCING_STATE_CHANGED);
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -23842,13 +22982,6 @@
DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
}
- static boolean isPolicyEngineForFinanceFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG,
- DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG);
- }
-
private static boolean isKeepProfilesRunningFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_DEVICE_POLICY_MANAGER,
@@ -24200,9 +23333,7 @@
}
private boolean shouldMigrateToDevicePolicyEngine() {
- return mInjector.binderWithCleanCallingIdentity(() ->
- (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled())
- && !mOwners.isMigratedToPolicyEngine());
+ return mInjector.binderWithCleanCallingIdentity(() -> !mOwners.isMigratedToPolicyEngine());
}
/**
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java
index 266f5c1..0ff4724 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java
@@ -39,11 +39,11 @@
DEFAULT_BRIGHTNESS * LOW_POWER_BRIGHTNESS_FACTOR;
private final DisplayPowerRequest mRequest = new DisplayPowerRequest();
private final DisplayBrightnessState.Builder mBuilder = prepareBuilder();
- private BrightnessLowPowerModeModifier mClamper;
+ private BrightnessLowPowerModeModifier mModifier;
@Before
public void setUp() {
- mClamper = new BrightnessLowPowerModeModifier();
+ mModifier = new BrightnessLowPowerModeModifier();
mRequest.screenLowPowerBrightnessFactor = LOW_POWER_BRIGHTNESS_FACTOR;
mRequest.lowPowerMode = true;
}
@@ -52,7 +52,7 @@
public void testApply_lowPowerModeOff() {
mRequest.lowPowerMode = false;
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(0, mBuilder.getBrightnessReason().getModifier());
@@ -61,7 +61,7 @@
@Test
public void testApply_lowPowerModeOn() {
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_LOW_POWER,
@@ -73,7 +73,7 @@
public void testApply_lowPowerModeOnAndLowPowerBrightnessFactorHigh() {
mRequest.screenLowPowerBrightnessFactor = 1.1f;
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_LOW_POWER,
@@ -84,7 +84,7 @@
@Test
public void testApply_lowPowerModeOnAndMinBrightness() {
mBuilder.setBrightness(0.0f);
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
assertEquals(0.0f, mBuilder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(0, mBuilder.getBrightnessReason().getModifier());
@@ -93,10 +93,10 @@
@Test
public void testApply_lowPowerModeOnAndLowPowerAlreadyApplied() {
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
DisplayBrightnessState.Builder builder = prepareBuilder();
- mClamper.apply(mRequest, builder);
+ mModifier.apply(mRequest, builder);
assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_LOW_POWER,
@@ -106,11 +106,11 @@
@Test
public void testApply_lowPowerModeOffAfterLowPowerOn() {
- mClamper.apply(mRequest, mBuilder);
+ mModifier.apply(mRequest, mBuilder);
mRequest.lowPowerMode = false;
DisplayBrightnessState.Builder builder = prepareBuilder();
- mClamper.apply(mRequest, builder);
+ mModifier.apply(mRequest, builder);
assertEquals(DEFAULT_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE);
assertEquals(0, builder.getBrightnessReason().getModifier());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java
new file mode 100644
index 0000000..be4e7c7
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.display.brightness.clamper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class DisplayDimModifierTest {
+ private static final float FLOAT_TOLERANCE = 0.001f;
+ private static final float DEFAULT_BRIGHTNESS = 0.5f;
+ private static final float MIN_DIM_AMOUNT = 0.05f;
+ private static final float DIM_CONFIG = 0.4f;
+
+ @Mock
+ private Context mMockContext;
+
+ @Mock
+ private PowerManager mMockPowerManager;
+
+ @Mock
+ private Resources mMockResources;
+
+ private final DisplayManagerInternal.DisplayPowerRequest
+ mRequest = new DisplayManagerInternal.DisplayPowerRequest();
+ private final DisplayBrightnessState.Builder mBuilder = prepareBuilder();
+ private DisplayDimModifier mModifier;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getFloat(
+ R.dimen.config_screenBrightnessMinimumDimAmountFloat)).thenReturn(MIN_DIM_AMOUNT);
+ when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
+ when(mMockPowerManager.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)).thenReturn(DIM_CONFIG);
+
+ mModifier = new DisplayDimModifier(mMockContext);
+ mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+ }
+
+ @Test
+ public void testApply_noDimPolicy() {
+ mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+ mModifier.apply(mRequest, mBuilder);
+
+ assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0, mBuilder.getBrightnessReason().getModifier());
+ assertTrue(mBuilder.isSlowChange());
+ }
+
+ @Test
+ public void testApply_dimPolicyFromResources() {
+ mBuilder.setBrightness(0.4f);
+ mModifier.apply(mRequest, mBuilder);
+
+ assertEquals(0.4f - MIN_DIM_AMOUNT, mBuilder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(BrightnessReason.MODIFIER_DIMMED,
+ mBuilder.getBrightnessReason().getModifier());
+ assertFalse(mBuilder.isSlowChange());
+ }
+
+ @Test
+ public void testApply_dimPolicyFromConfig() {
+ mModifier.apply(mRequest, mBuilder);
+
+ assertEquals(DIM_CONFIG, mBuilder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(BrightnessReason.MODIFIER_DIMMED,
+ mBuilder.getBrightnessReason().getModifier());
+ assertFalse(mBuilder.isSlowChange());
+ }
+
+ @Test
+ public void testApply_dimPolicyAndDimPolicyAlreadyApplied() {
+ mModifier.apply(mRequest, mBuilder);
+ DisplayBrightnessState.Builder builder = prepareBuilder();
+
+ mModifier.apply(mRequest, builder);
+
+ assertEquals(DIM_CONFIG, builder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(BrightnessReason.MODIFIER_DIMMED,
+ builder.getBrightnessReason().getModifier());
+ assertTrue(builder.isSlowChange());
+ }
+
+ @Test
+ public void testApply_dimPolicyAndMinBrightness() {
+ mBuilder.setBrightness(0.0f);
+ mModifier.apply(mRequest, mBuilder);
+
+ assertEquals(0.0f, mBuilder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0, mBuilder.getBrightnessReason().getModifier());
+ assertFalse(mBuilder.isSlowChange());
+ }
+
+ @Test
+ public void testApply_dimPolicyOffAfterDimPolicyOn() {
+ mModifier.apply(mRequest, mBuilder);
+ mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+ DisplayBrightnessState.Builder builder = prepareBuilder();
+
+ mModifier.apply(mRequest, builder);
+
+ assertEquals(DEFAULT_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0, builder.getBrightnessReason().getModifier());
+ assertFalse(builder.isSlowChange());
+ }
+
+ private DisplayBrightnessState.Builder prepareBuilder() {
+ DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder();
+ builder.setBrightness(DEFAULT_BRIGHTNESS);
+ builder.setIsSlowChange(true);
+ return builder;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index d32b6be..78e5a42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -331,6 +331,7 @@
@Test
public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception {
setSystemUserHeadless(true);
+ removeNonSystemUsers();
assertThrows(UserManager.CheckedUserOperationException.class,
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
@@ -504,6 +505,14 @@
.isFalse();
}
+ private void removeNonSystemUsers() {
+ for (UserInfo user : mUms.getUsers(true)) {
+ if (!user.getUserHandle().isSystem()) {
+ mUms.removeUserInfo(user.id);
+ }
+ }
+ }
+
private void resetUserSwitcherEnabled() {
mUms.putUserInfo(new UserInfo(USER_ID, "Test User", 0));
mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 58cdb1b..91d8ceb 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -50,9 +50,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.power.batterysaver.BatterySaverController;
-import com.android.server.power.batterysaver.BatterySaverPolicy;
-import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -69,8 +67,7 @@
private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
private static final int USER_ID = 0;
- @Mock private BatterySaverController mBatterySaverControllerMock;
- @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+ @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -263,16 +260,8 @@
}
@Override
- BatterySaverPolicy createBatterySaverPolicy(
- Object lock, Context context, BatterySavingStats batterySavingStats) {
- return mBatterySaverPolicyMock;
- }
-
- @Override
- BatterySaverController createBatterySaverController(
- Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
- BatterySavingStats batterySavingStats) {
- return mBatterySaverControllerMock;
+ BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
+ return mBatterySaverStateMachineMock;
}
@Override
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index d6d5264..8e1d8ab 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -108,7 +108,6 @@
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.batterysaver.BatterySavingStats;
import com.android.server.testutils.OffsettableClock;
import com.google.testing.junit.testparameterinjector.TestParameter;
@@ -184,6 +183,7 @@
private OffsettableClock mClock;
private long mLastElapsedRealtime;
private TestLooper mTestLooper;
+ private boolean mIsBatterySaverSupported = true;
private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
private final IntentFilter mFilter;
@@ -215,6 +215,10 @@
.setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
.setBrightnessFactor(BRIGHTNESS_FACTOR)
.build();
+ when(mBatterySaverStateMachineMock.getBatterySaverController()).thenReturn(
+ mBatterySaverControllerMock);
+ when(mBatterySaverStateMachineMock.getBatterySaverPolicy()).thenReturn(
+ mBatterySaverPolicyMock);
when(mBatterySaverPolicyMock.getBatterySaverPolicy(
eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
.thenReturn(powerSaveState);
@@ -235,6 +239,7 @@
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResourcesSpy = spy(mContextSpy.getResources());
when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+ setBatterySaverSupported();
MockContentResolver cr = new MockContentResolver(mContextSpy);
cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -269,21 +274,7 @@
}
@Override
- BatterySaverPolicy createBatterySaverPolicy(
- Object lock, Context context, BatterySavingStats batterySavingStats) {
- return mBatterySaverPolicyMock;
- }
-
- @Override
- BatterySaverController createBatterySaverController(
- Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
- BatterySavingStats batterySavingStats) {
- return mBatterySaverControllerMock;
- }
-
- @Override
- BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
- BatterySaverController batterySaverController) {
+ BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
return mBatterySaverStateMachineMock;
}
@@ -480,6 +471,12 @@
mTestLooper.dispatchAll();
}
+ private void setBatterySaverSupported() {
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_batterySaverSupported)).thenReturn(
+ mIsBatterySaverSupported);
+ }
+
@Test
public void testCreateService_initializesNativeServiceAndSetsPowerModes() {
PowerManagerService service = createService();
@@ -2543,6 +2540,19 @@
}
@Test
+ public void testGetFullPowerSavePolicy_whenNoBatterySaverSupported() {
+ mIsBatterySaverSupported = false;
+ setBatterySaverSupported();
+ createService();
+ BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
+ assertFalse(mService.getBinderServiceInstance().setPowerSaveModeEnabled(true));
+ BatterySaverPolicyConfig policyConfig =
+ mService.getBinderServiceInstance().getFullPowerSavePolicy();
+ assertThat(mockReturnConfig.toString()).isEqualTo(policyConfig.toString());
+ verify(mBatterySaverStateMachineMock, never()).getFullBatterySaverPolicy();
+ }
+
+ @Test
public void testSetFullPowerSavePolicy_callsStateMachine() {
createService();
startSystem();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 48ba765..f2cbef6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -38,7 +38,6 @@
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.Clock;
import org.junit.Before;
import org.junit.Test;
@@ -65,7 +64,7 @@
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
private File mHistoryDir;
- private final Clock mClock = new MockClock();
+ private final MockClock mClock = new MockClock();
private BatteryStatsHistory mHistory;
private BatteryStats.HistoryPrinter mHistoryPrinter;
@Mock
@@ -486,10 +485,94 @@
NetworkRegistrationInfo.NR_STATE_NONE);
}
+ @Test
+ public void largeTagPool() {
+ // Keep the preserved part of history short - we only need to capture the very tail of
+ // history.
+ mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 1, 6000,
+ mStepDetailsCalculator, mClock, mTracer);
+
+ mHistory.forceRecordAllHistory();
+
+ mClock.realtime = 2_000_000;
+ mClock.uptime = 1_000_000;
+ // More than 32k strings
+ final int tagCount = 0x7FFF + 20;
+ for (int tag = 0; tag < tagCount;) {
+ mClock.realtime += 10;
+ mClock.uptime += 10;
+ mHistory.recordEvent(mClock.realtime, mClock.uptime, HistoryItem.EVENT_ALARM_START,
+ "a" + (tag++), 42);
+
+ mHistory.setBatteryState(true, BatteryManager.BATTERY_STATUS_CHARGING, tag % 50, 0);
+ mClock.realtime += 10;
+ mClock.uptime += 10;
+ mHistory.recordWakelockStartEvent(mClock.realtime, mClock.uptime, "w" + tag, 42);
+ mClock.realtime += 10;
+ mClock.uptime += 10;
+ mHistory.recordWakelockStopEvent(mClock.realtime, mClock.uptime, "w" + tag, 42);
+ tag++;
+
+ mHistory.recordWakeupEvent(mClock.realtime, mClock.uptime, "wr" + (tag++));
+ }
+
+ int eventTagsPooled = 0;
+ int eventTagsUnpooled = 0;
+ int wakelockTagsPooled = 0;
+ int wakelockTagsUnpooled = 0;
+ int wakeReasonTagsPooled = 0;
+ int wakeReasonTagsUnpooled = 0;
+ for (BatteryStatsHistoryIterator iterator = mHistory.iterate(); iterator.hasNext(); ) {
+ HistoryItem item = iterator.next();
+ if (item.cmd != HistoryItem.CMD_UPDATE) {
+ continue;
+ }
+ String checkinDump = toString(item, true);
+ if (item.eventCode == HistoryItem.EVENT_ALARM_START) {
+ if (item.eventTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+ eventTagsPooled++;
+ assertThat(checkinDump).contains("+Eal=" + item.eventTag.poolIdx);
+ } else {
+ eventTagsUnpooled++;
+ assertThat(checkinDump).contains("+Eal=42:\"" + item.eventTag.string + "\"");
+ }
+ }
+
+ if (item.wakelockTag != null) {
+ if (item.wakelockTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+ wakelockTagsPooled++;
+ assertThat(checkinDump).contains("w=" + item.wakelockTag.poolIdx);
+ } else {
+ wakelockTagsUnpooled++;
+ assertThat(checkinDump).contains("w=42:\"" + item.wakelockTag.string + "\"");
+ }
+ }
+
+ if (item.wakeReasonTag != null) {
+ if (item.wakeReasonTag.poolIdx
+ != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+ wakeReasonTagsPooled++;
+ assertThat(checkinDump).contains("wr=" + item.wakeReasonTag.poolIdx);
+ } else {
+ wakeReasonTagsUnpooled++;
+ assertThat(checkinDump).contains("wr=0:\"" + item.wakeReasonTag.string + "\"");
+ }
+ }
+ }
+
+ // Self-check - ensure that we have all cases represented in the test
+ assertThat(eventTagsPooled).isGreaterThan(0);
+ assertThat(eventTagsUnpooled).isGreaterThan(0);
+ assertThat(wakelockTagsPooled).isGreaterThan(0);
+ assertThat(wakelockTagsUnpooled).isGreaterThan(0);
+ assertThat(wakeReasonTagsPooled).isGreaterThan(0);
+ assertThat(wakeReasonTagsUnpooled).isGreaterThan(0);
+ }
+
private String toString(BatteryStats.HistoryItem item, boolean checkin) {
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
- mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ true);
+ mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ false);
pw.flush();
return writer.toString();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 0cfddd3..769be17 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -351,6 +351,8 @@
assertEquals(startFingerprintNow ? BiometricSensor.STATE_AUTHENTICATING
: BiometricSensor.STATE_COOKIE_RETURNED,
session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
+ verify(mBiometricContext).updateContext((OperationContextExt) anyObject(),
+ eq(session.isCrypto()));
// start fingerprint sensor if it was delayed
if (!startFingerprintNow) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
new file mode 100644
index 0000000..99d66c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.biometrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AuthenticationStatsCollectorTest {
+
+ private AuthenticationStatsCollector mAuthenticationStatsCollector;
+ private static final float FRR_THRESHOLD = 0.2f;
+ private static final int USER_ID_1 = 1;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
+ .thenReturn(FRR_THRESHOLD);
+
+ mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
+ 0 /* modality */);
+ }
+
+
+ @Test
+ public void authenticate_authenticationSucceeded_mapShouldBeUpdated() {
+ // Assert that the user doesn't exist in the map initially.
+ assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1));
+
+ mAuthenticationStatsCollector.authenticate(USER_ID_1, true /* authenticated*/);
+
+ AuthenticationStats authenticationStats =
+ mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
+ assertEquals(USER_ID_1, authenticationStats.getUserId());
+ assertEquals(1, authenticationStats.getTotalAttempts());
+ assertEquals(0, authenticationStats.getRejectedAttempts());
+ assertEquals(0, authenticationStats.getEnrollmentNotifications());
+ }
+
+ @Test
+ public void authenticate_authenticationFailed_mapShouldBeUpdated() {
+ // Assert that the user doesn't exist in the map initially.
+ assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1));
+
+ mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated*/);
+
+ AuthenticationStats authenticationStats =
+ mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
+ assertEquals(USER_ID_1, authenticationStats.getUserId());
+ assertEquals(1, authenticationStats.getTotalAttempts());
+ assertEquals(1, authenticationStats.getRejectedAttempts());
+ assertEquals(0, authenticationStats.getEnrollmentNotifications());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsTest.java
new file mode 100644
index 0000000..e8e72cb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsTest.java
@@ -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.server.biometrics;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class AuthenticationStatsTest {
+
+ @Test
+ public void authenticate_statsShouldBeUpdated() {
+ AuthenticationStats authenticationStats =
+ new AuthenticationStats(1 /* userId */ , 0 /* totalAttempts */,
+ 0 /* rejectedAttempts */, 0 /* enrollmentNotifications */,
+ 0 /* modality */);
+
+ authenticationStats.authenticate(true /* authenticated */);
+
+ assertEquals(authenticationStats.getTotalAttempts(), 1);
+ assertEquals(authenticationStats.getRejectedAttempts(), 0);
+
+ authenticationStats.authenticate(false /* authenticated */);
+
+ assertEquals(authenticationStats.getTotalAttempts(), 2);
+ assertEquals(authenticationStats.getRejectedAttempts(), 1);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 612f717..a508718 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import org.junit.Before;
@@ -65,6 +66,8 @@
@Mock
private BiometricFrameworkStatsLogger mSink;
@Mock
+ private AuthenticationStatsCollector mAuthenticationStatsCollector;
+ @Mock
private SensorManager mSensorManager;
@Mock
private BaseClientMonitor mClient;
@@ -87,7 +90,8 @@
}
private BiometricLogger createLogger(int statsModality, int statsAction, int statsClient) {
- return new BiometricLogger(statsModality, statsAction, statsClient, mSink, mSensorManager);
+ return new BiometricLogger(statsModality, statsAction, statsClient, mSink,
+ mAuthenticationStatsCollector, mSensorManager);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index d1d6e9d..f43120d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
@@ -39,6 +40,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -59,11 +61,15 @@
private static final String TAG = "FaceProviderTest";
+ private static final float FRR_THRESHOLD = 0.2f;
+
@Mock
private Context mContext;
@Mock
private UserManager mUserManager;
@Mock
+ private Resources mResources;
+ @Mock
private IFace mDaemon;
@Mock
private BiometricContext mBiometricContext;
@@ -86,6 +92,10 @@
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
+ .thenReturn(FRR_THRESHOLD);
+
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
sensor1.commonProps.sensorId = 0;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index d174533..e558c4d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
@@ -41,6 +42,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -65,12 +67,15 @@
private static final String TAG = "Face10Test";
private static final int SENSOR_ID = 1;
private static final int USER_ID = 20;
+ private static final float FRR_THRESHOLD = 0.2f;
@Mock
private Context mContext;
@Mock
private UserManager mUserManager;
@Mock
+ private Resources mResources;
+ @Mock
private BiometricScheduler mScheduler;
@Mock
private BiometricContext mBiometricContext;
@@ -93,6 +98,10 @@
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
+ .thenReturn(FRR_THRESHOLD);
+
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
final int maxEnrollmentsPerUser = 1;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 40c762c..dec89d9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -383,6 +383,47 @@
}
@Test
+ public void handleRoutingChange_toSwitchInActivePath_noStandby() {
+ int newPlaybackPhysicalAddress = 0x2100;
+ int switchPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ newPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, newPlaybackPhysicalAddress,
+ switchPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ }
+
+ @Test
+ public void handleRoutingChange_toTv_StandbyNow() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, mPlaybackPhysicalAddress,
+ Constants.TV_PHYSICAL_ADDRESS);
+ assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
+ }
+
+ @Test
public void handleRoutingChange_otherDevice_StandbyNow_InactiveSource() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index dd681aa..cb659b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -130,7 +130,8 @@
// Keep system and current user
if (user.id != UserHandle.USER_SYSTEM &&
user.id != currentUser &&
- user.id != communalProfileId) {
+ user.id != communalProfileId &&
+ !user.isMain()) {
removeUser(user.id);
}
}
@@ -325,6 +326,24 @@
assertThat(hasUser(user2.id)).isTrue();
}
+
+ @MediumTest
+ @Test
+ public void testGetFullUserCount() throws Exception {
+ assertThat(mUserManager.getFullUserCount()).isEqualTo(1);
+ UserInfo user1 = createUser("User 1", UserInfo.FLAG_FULL);
+ UserInfo user2 = createUser("User 2", UserInfo.FLAG_ADMIN);
+
+ assertThat(user1).isNotNull();
+ assertThat(user2).isNotNull();
+
+ assertThat(mUserManager.getFullUserCount()).isEqualTo(3);
+ removeUser(user1.id);
+ assertThat(mUserManager.getFullUserCount()).isEqualTo(2);
+ removeUser(user2.id);
+ assertThat(mUserManager.getFullUserCount()).isEqualTo(1);
+ }
+
/**
* Tests that UserManager knows how many users can be created.
*
@@ -1105,16 +1124,16 @@
public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
assumeManagedUsersSupported();
final int mainUserId = mUserManager.getMainUser().getIdentifier();
- final UserHandle mainUserHandle = asHandle(mainUserId);
+ final UserHandle currentUserHandle = asHandle(ActivityManager.getCurrentUser());
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
- mainUserHandle);
+ currentUserHandle);
try {
UserInfo userInfo = createProfileForUser("Managed",
UserManager.USER_TYPE_PROFILE_MANAGED, mainUserId);
assertThat(userInfo).isNull();
} finally {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
- mainUserHandle);
+ currentUserHandle);
}
}
@@ -1190,6 +1209,7 @@
@Test
public void testGetManagedProfileCreationTime() throws Exception {
assumeManagedUsersSupported();
+ assumeTrue("User does not have access to creation time", mUserManager.isMainUser());
final int mainUserId = mUserManager.getMainUser().getIdentifier();
final long startTime = System.currentTimeMillis();
UserInfo profile = createProfileForUser("Managed 1",
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 726a4e2..9fca513 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -27,10 +27,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -73,6 +74,7 @@
private static final int TGID = Process.getThreadGroupLeader(TID);
private static final int[] SESSION_TIDS_A = new int[] {TID};
private static final int[] SESSION_TIDS_B = new int[] {TID};
+ private static final int[] SESSION_TIDS_C = new int[] {TID};
private static final long[] DURATIONS_THREE = new long[] {1L, 100L, 1000L};
private static final long[] TIMESTAMPS_THREE = new long[] {1L, 2L, 3L};
private static final long[] DURATIONS_ZERO = new long[] {};
@@ -94,6 +96,8 @@
eq(DEFAULT_TARGET_DURATION))).thenReturn(1L);
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_B),
eq(DEFAULT_TARGET_DURATION))).thenReturn(2L);
+ when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
+ eq(0L))).thenReturn(1L);
when(mAmInternalMock.getIsolatedProcesses(anyInt())).thenReturn(null);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
@@ -138,6 +142,10 @@
IHintSession b = service.getBinderServiceInstance().createHintSession(token,
SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
assertNotEquals(a, b);
+
+ IHintSession c = service.getBinderServiceInstance().createHintSession(token,
+ SESSION_TIDS_C, 0L);
+ assertNotNull(c);
}
@Test
@@ -338,4 +346,35 @@
a.setThreads(SESSION_TIDS_A);
verify(mNativeWrapperMock, never()).halSetThreads(anyLong(), any());
}
+
+ @Test
+ public void testSetMode() throws Exception {
+ HintManagerService service = createService();
+ IBinder token = new Binder();
+
+ AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
+ .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+
+ a.setMode(0, true);
+ verify(mNativeWrapperMock, times(1)).halSetMode(anyLong(),
+ eq(0), eq(true));
+
+ a.setMode(0, false);
+ verify(mNativeWrapperMock, times(1)).halSetMode(anyLong(),
+ eq(0), eq(false));
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ a.setMode(-1, true);
+ });
+
+ reset(mNativeWrapperMock);
+ // Set session to background, then the duration would not be updated.
+ service.mUidObserver.onUidStateChanged(
+ a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ FgThread.getHandler().runWithScissors(() -> { }, 500);
+ assertFalse(a.updateHintAllowed());
+ a.setMode(0, true);
+ verify(mNativeWrapperMock, never()).halSetMode(anyLong(), anyInt(), anyBoolean());
+ }
+
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 0eca8c9..98f1843 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -18,6 +18,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.Presubmit;
@@ -28,6 +31,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
/**
* Test class for {@link ActivitySnapshotController}.
*
@@ -42,13 +47,13 @@
private ActivitySnapshotController mActivitySnapshotController;
@Before
public void setUp() throws Exception {
+ spyOn(mWm.mSnapshotController.mActivitySnapshotController);
mActivitySnapshotController = mWm.mSnapshotController.mActivitySnapshotController;
+ doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
mActivitySnapshotController.resetTmpFields();
}
@Test
public void testOpenActivityTransition() {
- final SnapshotController.TransitionState transitionState =
- new SnapshotController.TransitionState();
final Task task = createTask(mDisplayContent);
// note for createAppWindow: the new child is added at index 0
final WindowState openingWindow = createAppWindow(task,
@@ -59,14 +64,12 @@
"closingWindow");
closingWindow.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- transitionState.addParticipant(closingWindow.mActivityRecord, false);
- transitionState.addParticipant(openingWindow.mActivityRecord, true);
- mActivitySnapshotController.handleOpenActivityTransition(transitionState);
+ final ArrayList<WindowContainer> windows = new ArrayList<>();
+ windows.add(openingWindow.mActivityRecord);
+ windows.add(closingWindow.mActivityRecord);
+ mActivitySnapshotController.handleTransitionFinish(windows);
- assertEquals(1, mActivitySnapshotController.mPendingCaptureActivity.size());
assertEquals(0, mActivitySnapshotController.mPendingRemoveActivity.size());
- assertEquals(closingWindow.mActivityRecord,
- mActivitySnapshotController.mPendingCaptureActivity.valueAt(0));
mActivitySnapshotController.resetTmpFields();
// simulate three activity
@@ -74,19 +77,15 @@
"belowClose");
belowClose.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- mActivitySnapshotController.handleOpenActivityTransition(transitionState);
- assertEquals(1, mActivitySnapshotController.mPendingCaptureActivity.size());
+ windows.add(belowClose.mActivityRecord);
+ mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size());
- assertEquals(closingWindow.mActivityRecord,
- mActivitySnapshotController.mPendingCaptureActivity.valueAt(0));
assertEquals(belowClose.mActivityRecord,
mActivitySnapshotController.mPendingRemoveActivity.valueAt(0));
}
@Test
public void testCloseActivityTransition() {
- final SnapshotController.TransitionState transitionState =
- new SnapshotController.TransitionState();
final Task task = createTask(mDisplayContent);
// note for createAppWindow: the new child is added at index 0
final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
@@ -97,10 +96,10 @@
ACTIVITY_TYPE_STANDARD, "openingWindow");
openingWindow.mActivityRecord.commitVisibility(
true /* visible */, true /* performLayout */);
- transitionState.addParticipant(closingWindow.mActivityRecord, false);
- transitionState.addParticipant(openingWindow.mActivityRecord, true);
- mActivitySnapshotController.handleCloseActivityTransition(transitionState);
- assertEquals(0, mActivitySnapshotController.mPendingCaptureActivity.size());
+ final ArrayList<WindowContainer> windows = new ArrayList<>();
+ windows.add(openingWindow.mActivityRecord);
+ windows.add(closingWindow.mActivityRecord);
+ mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingDeleteActivity.size());
assertEquals(openingWindow.mActivityRecord,
mActivitySnapshotController.mPendingDeleteActivity.valueAt(0));
@@ -111,8 +110,8 @@
"belowOpen");
belowOpen.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- mActivitySnapshotController.handleCloseActivityTransition(transitionState);
- assertEquals(0, mActivitySnapshotController.mPendingCaptureActivity.size());
+ windows.add(belowOpen.mActivityRecord);
+ mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingDeleteActivity.size());
assertEquals(1, mActivitySnapshotController.mPendingLoadActivity.size());
assertEquals(openingWindow.mActivityRecord,
@@ -123,10 +122,6 @@
@Test
public void testTaskTransition() {
- final SnapshotController.TransitionState taskCloseTransition =
- new SnapshotController.TransitionState();
- final SnapshotController.TransitionState taskOpenTransition =
- new SnapshotController.TransitionState();
final Task closeTask = createTask(mDisplayContent);
// note for createAppWindow: the new child is added at index 0
final WindowState closingWindow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD,
@@ -147,10 +142,10 @@
"openingWindowBelow");
openingWindowBelow.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- taskCloseTransition.addParticipant(closeTask, false);
- taskOpenTransition.addParticipant(openTask, true);
- mActivitySnapshotController.handleCloseTaskTransition(taskCloseTransition);
- mActivitySnapshotController.handleOpenTaskTransition(taskOpenTransition);
+ final ArrayList<WindowContainer> windows = new ArrayList<>();
+ windows.add(closeTask);
+ windows.add(openTask);
+ mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size());
assertEquals(closingWindowBelow.mActivityRecord,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4290f4b..d169a58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -490,6 +490,11 @@
.build();
final Task task = activity.getTask();
final TaskDisplayArea tda = task.getDisplayArea();
+ // Ensure the display is not a large screen
+ if (tda.getConfiguration().smallestScreenWidthDp
+ >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) {
+ resizeDisplay(activity.mDisplayContent, 500, 800);
+ }
// Ignore the activity min width/height for determine multi window eligibility.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java
deleted file mode 100644
index 83af1814..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-
-import static com.android.server.wm.SnapshotController.ACTIVITY_CLOSE;
-import static com.android.server.wm.SnapshotController.ACTIVITY_OPEN;
-import static com.android.server.wm.SnapshotController.TASK_CLOSE;
-import static com.android.server.wm.SnapshotController.TASK_OPEN;
-
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.util.ArraySet;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-
-/**
- * Test class for {@link SnapshotController}.
- *
- * Build/Install/Run:
- * * atest WmTests:AppSnapshotControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class AppSnapshotControllerTests extends WindowTestsBase {
- final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
- final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
-
- final TransitionMonitor mOpenActivityMonitor = new TransitionMonitor();
- final TransitionMonitor mCloseActivityMonitor = new TransitionMonitor();
- final TransitionMonitor mOpenTaskMonitor = new TransitionMonitor();
- final TransitionMonitor mCloseTaskMonitor = new TransitionMonitor();
-
- @Before
- public void setUp() throws Exception {
- resetStatus();
- mWm.mSnapshotController.registerTransitionStateConsumer(
- ACTIVITY_CLOSE, mCloseActivityMonitor::handleTransition);
- mWm.mSnapshotController.registerTransitionStateConsumer(
- ACTIVITY_OPEN, mOpenActivityMonitor::handleTransition);
- mWm.mSnapshotController.registerTransitionStateConsumer(
- TASK_CLOSE, mCloseTaskMonitor::handleTransition);
- mWm.mSnapshotController.registerTransitionStateConsumer(
- TASK_OPEN, mOpenTaskMonitor::handleTransition);
- }
-
- @After
- public void tearDown() throws Exception {
- mWm.mSnapshotController.unregisterTransitionStateConsumer(
- ACTIVITY_CLOSE, mCloseActivityMonitor::handleTransition);
- mWm.mSnapshotController.unregisterTransitionStateConsumer(
- ACTIVITY_OPEN, mOpenActivityMonitor::handleTransition);
- mWm.mSnapshotController.unregisterTransitionStateConsumer(
- TASK_CLOSE, mCloseTaskMonitor::handleTransition);
- mWm.mSnapshotController.unregisterTransitionStateConsumer(
- TASK_OPEN, mOpenTaskMonitor::handleTransition);
- }
-
- private static class TransitionMonitor {
- private final ArraySet<WindowContainer> mOpenParticipant = new ArraySet<>();
- private final ArraySet<WindowContainer> mCloseParticipant = new ArraySet<>();
- void handleTransition(SnapshotController.TransitionState<ActivityRecord> state) {
- mOpenParticipant.addAll(state.getParticipant(true /* open */));
- mCloseParticipant.addAll(state.getParticipant(false /* close */));
- }
- void reset() {
- mOpenParticipant.clear();
- mCloseParticipant.clear();
- }
- }
-
- private void resetStatus() {
- mClosingApps.clear();
- mOpeningApps.clear();
- mOpenActivityMonitor.reset();
- mCloseActivityMonitor.reset();
- mOpenTaskMonitor.reset();
- mCloseTaskMonitor.reset();
- }
-
- @Test
- public void testHandleAppTransition_openActivityTransition() {
- final Task task = createTask(mDisplayContent);
- // note for createAppWindow: the new child is added at index 0
- final WindowState openingWindow = createAppWindow(task,
- ACTIVITY_TYPE_STANDARD, "openingWindow");
- openingWindow.mActivityRecord.commitVisibility(
- true /* visible */, true /* performLayout */);
- final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
- "closingWindow");
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- mClosingApps.add(closingWindow.mActivityRecord);
- mOpeningApps.add(openingWindow.mActivityRecord);
- mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps);
- assertTrue(mOpenActivityMonitor.mCloseParticipant.contains(closingWindow.mActivityRecord));
- assertTrue(mOpenActivityMonitor.mOpenParticipant.contains(openingWindow.mActivityRecord));
- }
-
- @Test
- public void testHandleAppTransition_closeActivityTransition() {
- final Task task = createTask(mDisplayContent);
- // note for createAppWindow: the new child is added at index 0
- final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
- "closingWindow");
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- final WindowState openingWindow = createAppWindow(task,
- ACTIVITY_TYPE_STANDARD, "openingWindow");
- openingWindow.mActivityRecord.commitVisibility(
- true /* visible */, true /* performLayout */);
- mClosingApps.add(closingWindow.mActivityRecord);
- mOpeningApps.add(openingWindow.mActivityRecord);
- mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps);
- assertTrue(mCloseActivityMonitor.mCloseParticipant.contains(closingWindow.mActivityRecord));
- assertTrue(mCloseActivityMonitor.mOpenParticipant.contains(openingWindow.mActivityRecord));
- }
-
- @Test
- public void testHandleAppTransition_TaskTransition() {
- final Task closeTask = createTask(mDisplayContent);
- // note for createAppWindow: the new child is added at index 0
- final WindowState closingWindow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD,
- "closingWindow");
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
- final WindowState closingWindowBelow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD,
- "closingWindowBelow");
- closingWindowBelow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
-
- final Task openTask = createTask(mDisplayContent);
- final WindowState openingWindow = createAppWindow(openTask, ACTIVITY_TYPE_STANDARD,
- "openingWindow");
- openingWindow.mActivityRecord.commitVisibility(
- true /* visible */, true /* performLayout */);
- final WindowState openingWindowBelow = createAppWindow(openTask, ACTIVITY_TYPE_STANDARD,
- "openingWindowBelow");
- openingWindowBelow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
-
- mClosingApps.add(closingWindow.mActivityRecord);
- mOpeningApps.add(openingWindow.mActivityRecord);
- mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps);
- assertTrue(mCloseTaskMonitor.mCloseParticipant.contains(closeTask));
- assertTrue(mOpenTaskMonitor.mOpenParticipant.contains(openTask));
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 41c0caae..c84eab3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -36,6 +36,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import android.app.WindowConfiguration;
@@ -507,6 +508,7 @@
// Return the default display as the value to mirror to ensure the VD with flag mirroring
// creates a ContentRecordingSession automatically.
doReturn(DEFAULT_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+ clearInvocations(virtualDisplay);
virtualDisplay.updateRecording();
// THEN mirroring is initiated for the default display's DisplayArea.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index f536cd0..87dbca5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -114,8 +114,9 @@
@Test
public void testResolveOverrideConfiguration_reverseOrientationWhenDifferentFromParentRoot() {
- mDisplayContent.setBounds(0, 0, 600, 900);
- mDisplayContent.updateOrientation();
+ // Rotate the display to portrait.
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ displayRotation.setRotation(displayRotation.getPortraitRotation());
mDisplayContent.sendNewConfiguration();
// DAG fills Display
@@ -128,7 +129,7 @@
assertThat(mDisplayAreaGroup.getConfiguration().orientation)
.isEqualTo(ORIENTATION_LANDSCAPE);
- // DisplayAreaGroup is portriat, same as Display
+ // DisplayAreaGroup is portrait, same as Display
mDisplayAreaGroup.setBounds(0, 0, 300, 450);
assertThat(mDisplayAreaGroup.getConfiguration().orientation)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1c0fd4f..a2b7da3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1819,7 +1819,7 @@
final ActivityRecord activity = createActivityRecord(mDisplayContent);
final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
- recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
// Do not rotate if the recents animation is animating on top.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 6e52af1..c1be5ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -353,6 +353,7 @@
@Test
public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+ makeDisplayPortrait(mDefaultDisplay);
unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
@@ -488,6 +489,7 @@
@Test
public void testWallpaperHasFixedRotationApplied() {
+ makeDisplayPortrait(mDefaultDisplay);
unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index be436bf..7634d9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -384,7 +384,16 @@
}
private void tearDown() {
- mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release);
+ for (int i = mWmService.mRoot.getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent dc = mWmService.mRoot.getChildAt(i);
+ // Unregister SettingsObserver.
+ dc.getDisplayPolicy().release();
+ // Unregister SensorEventListener (foldable device may register for hinge angle).
+ dc.getDisplayRotation().onDisplayRemoved();
+ if (dc.mDisplayRotationCompatPolicy != null) {
+ dc.mDisplayRotationCompatPolicy.dispose();
+ }
+ }
// Unregister display listener from root to avoid issues with subsequent tests.
mContext.getSystemService(DisplayManager.class)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 54b9351..bfa279d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -143,6 +143,7 @@
@Before
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ removeGlobalMinSizeRestriction();
mWindowOrganizerController = mAtm.mWindowOrganizerController;
mTransitionController = mWindowOrganizerController.mTransitionController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 40b1521..07cdfaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -46,7 +46,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.SnapshotController.TASK_CLOSE;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -1387,8 +1386,6 @@
@Test
public void testTransientLaunch() {
spyOn(mWm.mSnapshotController.mTaskSnapshotController);
- mWm.mSnapshotController.registerTransitionStateConsumer(TASK_CLOSE,
- mWm.mSnapshotController.mTaskSnapshotController::handleTaskClose);
final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
final TransitionController controller = new TestTransitionController(mAtm) {
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 6305bb6..6216acb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -116,10 +116,7 @@
public void testWallpaperSizeWithFixedTransform() {
// No wallpaper
final DisplayContent dc = mDisplayContent;
- if (dc.mBaseDisplayHeight == dc.mBaseDisplayWidth) {
- // Make sure the size is different when changing orientation.
- resizeDisplay(dc, 500, 1000);
- }
+ makeDisplayPortrait(dc);
// No wallpaper WSA Surface
final WindowState wallpaperWindow = createWallpaperWindow(dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 55fda05..76576f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -107,6 +107,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+
/**
* Build/Install/Run:
* atest WmTests:WindowManagerServiceTests
@@ -975,6 +977,28 @@
verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0);
}
+ @Test
+ public void testReportSystemGestureExclusionChanged_invalidWindow() {
+ final Session session = mock(Session.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder binder = mock(IBinder.class);
+ doReturn(binder).when(window).asBinder();
+
+ // No exception even if the window doesn't exist
+ mWm.reportSystemGestureExclusionChanged(session, window, new ArrayList<>());
+ }
+
+ @Test
+ public void testReportKeepClearAreasChanged_invalidWindow() {
+ final Session session = mock(Session.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder binder = mock(IBinder.class);
+ doReturn(binder).when(window).asBinder();
+
+ // No exception even if the window doesn't exist
+ mWm.reportKeepClearAreasChanged(session, window, new ArrayList<>(), new ArrayList<>());
+ }
+
class TestResultReceiver implements IResultReceiver {
public android.os.Bundle resultData;
private final IBinder mBinder = mock(IBinder.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index cf83981..ebe40b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -170,9 +170,13 @@
}
@Test
- public void testSetRunningRecentsAnimation() {
- mWpc.setRunningRecentsAnimation(true);
- mWpc.setRunningRecentsAnimation(false);
+ public void testSetAnimatingReason() {
+ mWpc.addAnimatingReason(WindowProcessController.ANIMATING_REASON_REMOTE_ANIMATION);
+ assertTrue(mWpc.isRunningRemoteTransition());
+ mWpc.addAnimatingReason(WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
+ mWpc.removeAnimatingReason(WindowProcessController.ANIMATING_REASON_REMOTE_ANIMATION);
+ assertFalse(mWpc.isRunningRemoteTransition());
+ mWpc.removeAnimatingReason(WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
waitHandlerIdle(mAtm.mH);
InOrder orderVerifier = Mockito.inOrder(mMockListener);
@@ -201,7 +205,7 @@
waitHandlerIdle(mAtm.mH);
InOrder orderVerifier = Mockito.inOrder(mMockListener);
- orderVerifier.verify(mMockListener, times(3)).setRunningRemoteAnimation(eq(true));
+ orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(true));
orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(false));
orderVerifier.verifyNoMoreInteractions();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ba3b02a..ae7b161 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -973,11 +973,19 @@
static void resizeDisplay(DisplayContent displayContent, int width, int height) {
displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity,
displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi);
+ displayContent.getDisplayRotation().configure(width, height);
final Configuration c = new Configuration();
displayContent.computeScreenConfiguration(c);
displayContent.onRequestedOverrideConfigurationChanged(c);
}
+ /** Used for the tests that assume the display is portrait by default. */
+ static void makeDisplayPortrait(DisplayContent displayContent) {
+ if (displayContent.mBaseDisplayHeight <= displayContent.mBaseDisplayWidth) {
+ resizeDisplay(displayContent, 500, 1000);
+ }
+ }
+
// The window definition for UseTestDisplay#addWindows. The test can declare to add only
// necessary windows, that avoids adding unnecessary overhead of unused windows.
static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE;
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 3e67286..2ccc0fa 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -39,7 +39,11 @@
"src/**/activityembedding/*.kt",
"src/**/activityembedding/open/*.kt",
"src/**/activityembedding/close/*.kt",
+ "src/**/activityembedding/layoutchange/*.kt",
+ "src/**/activityembedding/pip/*.kt",
"src/**/activityembedding/rotation/*.kt",
+ "src/**/activityembedding/rtl/*.kt",
+ "src/**/activityembedding/splitscreen/*.kt",
],
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
index 4530ef3..0c36c59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
@@ -38,7 +38,7 @@
* Setup: Launch A|B in split with B being the secondary activity. Transitions: Finish B and expect
* A to become fullscreen.
*
- * To run this test: `atest FlickerTests:CloseSecondaryActivityInSplitTest`
+ * To run this test: `atest FlickerTestsOther:CloseSecondaryActivityInSplitTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index c3529ba..adff579 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.layoutchange
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,7 +39,7 @@
* windows are equal in size. B is on the top and A is on the bottom. Transitions: Change the split
* ratio to A:B=0.7:0.3, expect bounds change for both A and B.
*
- * To run this test: `atest FlickerTests:HorizontalSplitChangeRatioTest`
+ * To run this test: `atest FlickerTestsOther:HorizontalSplitChangeRatioTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -148,6 +149,7 @@
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
+
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 244c5dc..ce9c337 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -16,13 +16,13 @@
package com.android.server.wm.flicker.activityembedding.open
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
@@ -39,7 +39,7 @@
* Setup: Launch A|B in split with B being the secondary activity. Transitions: A start C with
* alwaysExpand=true, expect C to launch in fullscreen and cover split A|B.
*
- * To run this test: `atest FlickerTests:MainActivityStartsSecondaryWithAlwaysExpandTest`
+ * To run this test: `atest FlickerTestsOther:MainActivityStartsSecondaryWithAlwaysExpandTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -133,6 +133,7 @@
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
+
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 8a997dd..48edf6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -34,7 +34,7 @@
* Test opening an activity that will launch another activity as ActivityEmbedding placeholder in
* split.
*
- * To run this test: `atest FlickerTests:OpenActivityEmbeddingPlaceholderSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingPlaceholderSplitTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 49aa84b..3657820 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -34,7 +34,7 @@
/**
* Test opening a secondary activity that will split with the main activity.
*
- * To run this test: `atest FlickerTests:OpenActivityEmbeddingSecondaryToSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingSecondaryToSplitTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
index 4bb2246..9f9fc23 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -39,7 +39,7 @@
*
* Transitions: Let B start C, expect C to cover B and end up in split A|C.
*
- * To run this test: `atest FlickerTests:OpenThirdActivityOverSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenThirdActivityOverSplitTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index f409c4e..30e833f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.activityembedding.open
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
import android.tools.common.flicker.subject.region.RegionSubject
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
@@ -41,7 +41,7 @@
* Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes
* itself, end up in split A|B.
*
- * To run this test: `atest FlickerTests:OpenTrampolineActivityTest`
+ * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index e4c35b2..359845d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.pip
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
@@ -25,6 +25,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -38,7 +39,7 @@
* Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
* right corner on screen.
*
- * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest`
+ * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index da56500..4f7d8a4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -23,6 +23,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.rotation.RotationTransition
import org.junit.FixMethodOrder
@@ -37,7 +38,7 @@
* Setup: Launch A|B in split with B being the secondary activity. Transitions: Rotate display, and
* expect A and B to split evenly in new rotation.
*
- * To run this test: `atest FlickerTests:RotateSplitNoChangeTest`
+ * To run this test: `atest FlickerTestsOther:RotateSplitNoChangeTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
index 4bc17ed..6be78f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.rtl
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -22,6 +22,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,7 +37,7 @@
* PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. Expect split
* PlaceholderSecondary|PlaceholderPrimary covering split B|A.
*
- * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest`
+ * To run this test: `atest FlickerTestsOther:RTLStartSecondaryWithPlaceholderTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 71db76e..288558ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,12 +16,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 8dd7e65..32305c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,12 +16,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8737edb..8d752cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
@@ -24,6 +23,7 @@
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
index 2a2b70e..6311678 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
@@ -29,4 +29,4 @@
launcherName: String = ActivityOptions.TransferSplashscreenActivity.LABEL,
component: ComponentNameMatcher =
ActivityOptions.TransferSplashscreenActivity.COMPONENT.toFlickerComponent()
-) : StandardAppHelper(instr, launcherName, component)
\ No newline at end of file
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 99858ce..b44f1a6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index ec792ca2..48d5041 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -16,13 +16,13 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.FlakyTest
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,7 +54,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) :
- OpenAppFromLauncherTransition(flicker) {
+ OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 0197e66..78b58f4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -16,13 +16,13 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
index 36e66c7..cc501e6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
@@ -16,11 +16,11 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index e0fb751..3f931c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -25,6 +24,7 @@
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index f1cd69a..b85362a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.flicker.annotation.FlickerServiceCompatible
@@ -24,6 +23,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
index 1fdef3c..3d9c067 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -47,13 +47,12 @@
* 2. Verify no flickering when transfer splash screen to app window.
* ```
*/
-
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenTransferSplashscreenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
- OpenAppFromIconColdTest(flicker) {
+ OpenAppFromIconColdTest(flicker) {
override val testApp = TransferSplashscreenAppHelper(instrumentation)
/**
@@ -66,11 +65,11 @@
fun appWindowAfterSplash() {
flicker.assertWm {
this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
- .then()
- .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN)
- .then()
- .isAppWindowOnTop(testApp)
- .isAppWindowInvisible(ComponentNameMatcher.SPLASH_SCREEN)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN)
+ .then()
+ .isAppWindowOnTop(testApp)
+ .isAppWindowInvisible(ComponentNameMatcher.SPLASH_SCREEN)
}
}
@@ -85,4 +84,4 @@
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index df9780e..b82a129 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -19,7 +19,6 @@
import android.app.Instrumentation
import android.app.WallpaperManager
import android.content.res.Resources
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
@@ -33,6 +32,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NewTasksAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index bf0f4ab..9722216 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.notification
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
@@ -25,6 +24,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.ClassRule
@@ -174,7 +174,7 @@
val disableUnseenNotifFilterRule =
SettingOverrideRule(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- /* value= */ "0",
+ "0",
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index 3f3542d..ffd8171 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.notification
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
@@ -25,6 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 50ff62b..13fcc2b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
@@ -25,6 +24,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index aee9163..c090415 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
@@ -25,6 +24,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index d6a951d..f51be90 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -26,6 +25,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder