Merge "Ambient Activation p2.3" 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..c37ff97 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",
@@ -572,6 +588,170 @@
},
}
+//
+// Java API defaults and libraries for single tree build
+//
+
+java_defaults {
+ name: "stub-annotation-defaults",
+ libs: [
+ "stub-annotations",
+ ],
+ static_libs: [
+ // stub annotations do not contribute to the API surfaces but are statically
+ // linked in the stubs for API surfaces (see frameworks/base/StubLibraries.bp).
+ // This is because annotation processors insist on loading the classes for any
+ // annotations found, thus should exist inside android.jar.
+ "private-stub-annotations-jar",
+ ],
+}
+
+// Listing of API domains contribution and dependencies per API surfaces
+java_defaults {
+ name: "android_test_stubs_current_contributions",
+ api_surface: "test",
+ api_contributions: [
+ "test-api-stubs-docs-non-updatable.api.contribution",
+ "framework-virtualization.stubs.source.test.api.contribution",
+ ],
+}
+
+java_defaults {
+ name: "android_test_frameworks_core_stubs_current_contributions",
+ api_surface: "test",
+ api_contributions: [
+ "test-api-stubs-docs-non-updatable.api.contribution",
+ ],
+}
+
+java_defaults {
+ name: "android_module_lib_stubs_current_contributions",
+ api_surface: "module-lib",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ "module-lib-api-stubs-docs-non-updatable.api.contribution",
+ "art.module.public.api.stubs.source.api.contribution",
+ "art.module.public.api.stubs.source.system.api.contribution",
+ "art.module.public.api.stubs.source.module_lib.api.contribution",
+ "i18n.module.public.api.stubs.source.api.contribution",
+ "i18n.module.public.api.stubs.source.system.api.contribution",
+ "i18n.module.public.api.stubs.source.module_lib.api.contribution",
+ ],
+}
+
+// Java API library definitions per API surface
+java_api_library {
+ name: "android_stubs_current.from-text",
+ api_surface: "public",
+ defaults: [
+ // This module is dynamically created at frameworks/base/api/api.go
+ // instead of being written out, in order to minimize edits in the codebase
+ // when there is a change in the list of modules.
+ // that contributes to an api surface.
+ "android_stubs_current_contributions",
+ "stub-annotation-defaults",
+ ],
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_api_library {
+ name: "android_system_stubs_current.from-text",
+ api_surface: "system",
+ defaults: [
+ "android_stubs_current_contributions",
+ "android_system_stubs_current_contributions",
+ "stub-annotation-defaults",
+ ],
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_api_library {
+ name: "android_test_stubs_current.from-text",
+ api_surface: "test",
+ defaults: [
+ "android_stubs_current_contributions",
+ "android_system_stubs_current_contributions",
+ "android_test_stubs_current_contributions",
+ "stub-annotation-defaults",
+ ],
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_api_library {
+ name: "android_test_frameworks_core_stubs_current.from-text",
+ api_surface: "test",
+ defaults: [
+ "android_stubs_current_contributions",
+ "android_system_stubs_current_contributions",
+ "android_test_frameworks_core_stubs_current_contributions",
+ "stub-annotation-defaults",
+ ],
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ ],
+}
+
+java_api_library {
+ name: "android_module_lib_stubs_current_full.from-text",
+ api_surface: "module-lib",
+ defaults: [
+ "android_stubs_current_contributions",
+ "android_system_stubs_current_contributions",
+ "android_module_lib_stubs_current_contributions_full",
+ ],
+ libs: [
+ "stub-annotations",
+ ],
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ "module-lib-api-stubs-docs-non-updatable.api.contribution",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_api_library {
+ name: "android_module_lib_stubs_current.from-text",
+ api_surface: "module-lib",
+ defaults: [
+ "android_module_lib_stubs_current_contributions",
+ ],
+ libs: [
+ "android_module_lib_stubs_current_full.from-text",
+ "stub-annotations",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_api_library {
+ name: "android_system_server_stubs_current.from-text",
+ api_surface: "system-server",
+ api_contributions: [
+ "services-non-updatable-stubs.api.contribution",
+ ],
+ libs: [
+ "android_module_lib_stubs_current.from-text",
+ "stub-annotations",
+ ],
+ static_libs: [
+ "android_module_lib_stubs_current.from-text",
+ ],
+ visibility: ["//visibility:public"],
+}
+
////////////////////////////////////////////////////////////////////////
// api-versions.xml generation, for public and system. This API database
// also contains the android.test.* APIs.
diff --git a/api/api.go b/api/api.go
index a003aba..d5c6145 100644
--- a/api/api.go
+++ b/api/api.go
@@ -146,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" {
@@ -156,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{
{
@@ -370,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 363e9d4..9ba84c9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -47271,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);
@@ -47286,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);
@@ -47316,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);
@@ -47489,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);
@@ -47513,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
@@ -47563,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..0255860 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);
@@ -2872,16 +2876,25 @@
final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
: null;
- return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
+ return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras,
+ dialogInfo, 0);
}
@Override
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo) {
+ return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras,
+ dialogInfo, 0);
+ }
+
+ @Override
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras,
+ SuspendDialogInfo dialogInfo, int flags) {
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
- launcherExtras, dialogInfo, mContext.getOpPackageName(),
+ launcherExtras, dialogInfo, flags, mContext.getOpPackageName(),
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -3926,6 +3939,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 +3957,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/content/Context.java b/core/java/android/content/Context.java
index c11a8fc..5ef7b11 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -323,6 +323,7 @@
// Make sure no flag uses the sign bit (most significant bit) of the long integer,
// to avoid future confusion.
BIND_BYPASS_USER_NETWORK_RESTRICTIONS,
+ BIND_FILTER_OUT_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlagsLongBits {}
@@ -697,6 +698,13 @@
*/
public static final long BIND_BYPASS_USER_NETWORK_RESTRICTIONS = 0x1_0000_0000L;
+ /**
+ * Flag for {@link #bindService}.
+ *
+ * @hide
+ */
+ public static final long BIND_FILTER_OUT_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
+
/**
* These bind flags reduce the strength of the binding such that we shouldn't
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fe108a5..afeb3d29 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6383,6 +6383,15 @@
"android.intent.extra.changed_uid_list";
/**
+ * This field is part of
+ * {@link android.content.Intent#ACTION_PACKAGES_SUSPENDED},
+ * and only present if the packages were quarantined.
+ * @hide
+ */
+ public static final String EXTRA_QUARANTINED =
+ "android.intent.extra.quarantined";
+
+ /**
* An integer denoting a bitwise combination of restrictions set on distracting packages via
* {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
*
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7c9ccba..c5585af 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -298,7 +298,7 @@
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
- in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
+ in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId);
String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f14c97..885e67e1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -834,6 +834,7 @@
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
+ FILTER_OUT_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentInfoFlagsBits {}
@@ -857,7 +858,8 @@
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
- MATCH_CLONE_PROFILE
+ MATCH_CLONE_PROFILE,
+ FILTER_OUT_QUARANTINED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResolveInfoFlagsBits {}
@@ -1233,6 +1235,11 @@
public static final long GET_ATTRIBUTIONS_LONG = 0x80000000L;
/**
+ * @hide
+ */
+ public static final long FILTER_OUT_QUARANTINED_COMPONENTS = 0x100000000L;
+
+ /**
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
* resolving an intent that matches the {@code CrossProfileIntentFilter},
* the current profile will be skipped. Only activities in the target user
@@ -1685,7 +1692,7 @@
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
- SYNCHRONOUS
+ SYNCHRONOUS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EnabledFlags {}
@@ -1708,6 +1715,24 @@
public static final int SYNCHRONOUS = 0x00000002;
/** @hide */
+ @IntDef(flag = true, value = {
+ FLAG_SUSPEND_QUARANTINED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SuspendedFlags {}
+
+ /**
+ * Flag parameter {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, android.content.pm.SuspendDialogInfo, int)}:
+ * Apps in this state not only appear suspended for all user visible purposes (eg, Launcher,
+ * ShareSheet), but also individual components of the app can behave as disabled depending on
+ * the importance of the calling app.
+ *
+ * @hide
+ */
+ public static final int FLAG_SUSPEND_QUARANTINED = 0x00000001;
+
+ /** @hide */
@IntDef(prefix = { "INSTALL_REASON_" }, value = {
INSTALL_REASON_UNKNOWN,
INSTALL_REASON_POLICY,
@@ -9654,6 +9679,63 @@
}
/**
+ * Puts the given packages in a suspended state, where attempts at starting activities are
+ * denied.
+ *
+ * <p>The suspended application's notifications and all of its windows will be hidden, any
+ * of its started activities will be stopped and it won't be able to ring the device.
+ * It doesn't remove the data or the actual package file.
+ *
+ * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
+ * is suspended will be shown instead.
+ * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
+ * to this API. This dialog will have a button that starts the
+ * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
+ * activity which handles this action.
+ *
+ * <p>The packages being suspended must already be installed. If a package is uninstalled, it
+ * will no longer be suspended.
+ *
+ * <p>Optionally, the suspending app can provide extra information in the form of
+ * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+ * launcher to support customization that they might need to handle the suspended state.
+ *
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API except for
+ * device owner and profile owner.
+ *
+ * @param packageNames The names of the packages to set the suspended status.
+ * @param suspended If set to {@code true}, the packages will be suspended, if set to
+ * {@code false}, the packages will be unsuspended.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+ * should be shown to the user when they try to launch a suspended app.
+ * Ignored if {@code suspended} is false.
+ * @param flags Optional behavior flags.
+ *
+ * @return an array of package names for which the suspended status could not be set as
+ * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+ *
+ * @see #isPackageSuspended
+ * @see SuspendDialogInfo
+ * @see SuspendDialogInfo.Builder
+ * @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
+ *
+ * @hide
+ */
+ @RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true)
+ @Nullable
+ public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ @Nullable SuspendDialogInfo dialogInfo, @SuspendedFlags int flags) {
+ throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+ }
+
+ /**
* Returns any packages in a given set of packages that cannot be suspended via a call to {@link
* #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
* SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 048289f..9387ae1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8005,7 +8005,7 @@
ai.enabled = true;
} else if (state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+ ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
} else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|| state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
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/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/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/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 2e62e03..bf6e1c4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10126,6 +10126,13 @@
public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory";
/**
+ * Stores a boolean that defines whether the CSD as a feature is enabled or not.
+ * @hide
+ */
+ public static final String AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED =
+ "audio_safe_csd_as_a_feature_enabled";
+
+ /**
* Indicates whether notification display on the lock screen is enabled.
* <p>
* Type: int (0 for false, 1 for true)
@@ -11265,6 +11272,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
*/
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/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/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/WindowManager.java b/core/java/android/view/WindowManager.java
index 5d1a81f..62e37a4 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
@@ -3839,6 +3839,7 @@
* @see #ROTATION_ANIMATION_ROTATE
* @see #ROTATION_ANIMATION_CROSSFADE
* @see #ROTATION_ANIMATION_JUMPCUT
+ * @see #ROTATION_ANIMATION_SEAMLESS
*/
public int rotationAnimation = ROTATION_ANIMATION_ROTATE;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7a96fd2..e1de05b 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6948,6 +6948,7 @@
// something is going to start.
opts.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
return Pair.create(intent, opts);
}
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index a5e775a..1e0b2a0 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -297,7 +297,7 @@
final IPackageManager ipm = AppGlobals.getPackageManager();
try {
final String[] errored = ipm.setPackagesSuspendedAsUser(
- new String[]{mSuspendedPackage}, false, null, null, null,
+ new String[]{mSuspendedPackage}, false, null, null, null, 0,
mSuspendingPackage, mUserId);
if (ArrayUtils.contains(errored, mSuspendedPackage)) {
Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage);
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_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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 67710f6..e129f7d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7863,12 +7863,6 @@
</intent-filter>
</activity>
- <activity android:name="com.android.internal.app.NetInitiatedActivity"
- android:theme="@style/Theme.Dialog.Confirmation"
- android:excludeFromRecents="true"
- android:process=":ui">
- </activity>
-
<activity android:name="com.android.internal.app.SystemUserHomeActivity"
android:enabled="false"
android:process=":ui"
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/attrs.xml b/core/res/res/values/attrs.xml
index d80cfa3..f45499a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2465,7 +2465,7 @@
duration of the vector animation automatically. -->
<attr name="windowSplashScreenAnimationDuration" format="integer"/>
- <!-- Place an drawable image in the bottom of the starting window, it can be used to
+ <!-- Place a drawable image in the bottom of the starting window. The image can be used to
represent the branding of the application. -->
<attr name="windowSplashScreenBrandingImage" format="reference"/>
<!-- Set a background behind the splash screen icon. This is useful if there is not enough
@@ -3245,7 +3245,7 @@
<!-- Specifies the id of a view for which this view serves as a label for
accessibility purposes. For example, a TextView before an EditText in
- the UI usually specifies what infomation is contained in the EditText.
+ the UI usually specifies what information is contained in the EditText.
Hence, the TextView is a label for the EditText. -->
<attr name="labelFor" format="reference" />
@@ -6787,7 +6787,7 @@
edges of a bitmap when rotated. Default value is false. -->
<attr name="antialias" format="boolean" />
<!-- Enables or disables bitmap filtering. Filtering is used when the bitmap is
- shrunk or stretched to smooth its apperance. Default value is true. -->
+ shrunk or stretched to smooth its appearance. Default value is true. -->
<attr name="filter" format="boolean" />
<!-- Enables or disables dithering of the bitmap if the bitmap does not have the
same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b7d088b..95f1493 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -634,7 +634,7 @@
able to return to it. -->
<attr name="noHistory" format="boolean" />
- <!-- Specify whether an acitivty's task state should always be maintained
+ <!-- Specify whether an activity's task state should always be maintained
by the system, or if it is allowed to reset the task to its initial
state in certain situations.
@@ -731,15 +731,17 @@
This is equivalent to calling {@link android.app.Activity#setVrModeEnabled} with the
the given component name within the Activity that this attribute is set for.
Declaring this will prevent the system from leaving VR mode during an Activity
- transtion from one VR activity to another. -->
+ transition from one VR activity to another. -->
<attr name="enableVrMode" format="string" />
- <!-- Flag allowing the activity to specify which screen rotation animation
- it desires. Valid values are "rotate", "crossfade", and "jumpcut"
- as described in {@link android.view.WindowManager.LayoutParams#rotationAnimation}.
- Specifying your Rotation animation in the WindowManager.LayoutParams
- may be racy with app startup and updattransitions occuring during application startup and thusly
- the manifest attribute is preferred.
+ <!-- Flag that specifies the activity's preferred screen rotation animation.
+ Valid values are "rotate", "crossfade", "jumpcut", and "seamless" as
+ described in
+ {@link android.view.WindowManager.LayoutParams#rotationAnimation}.
+ Specifying your rotation animation in
+ <code>WindowManager.LayoutParams</code> may be racy with app startup
+ and update transitions that occur during application startup; and so,
+ specify the animation in the manifest attribute.
-->
<attr name="rotationAnimation">
<flag name="rotate" value= "0" />
@@ -830,7 +832,7 @@
<enum name="singleInstance" value="3" />
<!-- The activity can only be running as the root activity of the task, the first activity
that created the task, and therefore there will only be one instance of this activity
- in a task. In constrast to the {@code singleTask} launch mode, this activity can be
+ in a task. In contrast to the {@code singleTask} launch mode, this activity can be
started in multiple instances in different tasks if the
{@code FLAG_ACTIVITY_MULTIPLE_TASK} or {@code FLAG_ACTIVITY_NEW_DOCUMENT} is set.-->
<enum name="singleInstancePerTask" value="4" />
@@ -1328,7 +1330,7 @@
<p>Such a document is any kind of item for which an application may want to
maintain multiple simultaneous instances. Examples might be text files, web
pages, spreadsheets, or emails. Each such document will be in a separate
- task in the recent taskss list.
+ task in the recent tasks list.
<p>This attribute is equivalent to adding the flag {@link
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
@@ -1771,7 +1773,7 @@
</attr>
<!-- Enable hardware memory tagging (ARM MTE) in this process.
- When enabled, heap memory bugs like use-after-free and buffer overlow
+ When enabled, heap memory bugs like use-after-free and buffer overflow
are detected and result in an immediate ("sync" mode) or delayed ("async"
mode) crash instead of a silent memory corruption. Sync mode, while slower,
provides enhanced bug reports including stack traces at the time of allocation
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 72333fb..9621f93 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3036,14 +3036,15 @@
on the headphone/microphone jack. When false use the older uevent framework. -->
<bool name="config_useDevInputEventForAudioJack">false</bool>
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <!-- Whether safe headphone hearing is enforced by any regulation (e.g.
+ EN50332-3, EN50332-2) or not (country specific). -->
<bool name="config_safe_media_volume_enabled">true</bool>
- <!-- Whether safe headphone sound dosage warning is enabled or not
- (country specific). This value should only be overlaid to true
- when a vendor supports offload and has the HAL sound dose
- interfaces implemented. Otherwise, this can lead to a compliance
- issue with the safe hearing standards EN50332-3 and IEC62368-1.
+ <!-- Whether safe headphone sound dosage warning is enabled or not.
+ This value should only be overlaid to true when a vendor supports
+ offload and has the HAL sound dose interfaces implemented.
+ Otherwise, this can lead to a compliance issue with the safe
+ hearing standards EN50332-3 and IEC62368-1.
-->
<bool name="config_safe_sound_dosage_enabled">false</bool>
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/symbols.xml b/core/res/res/values/symbols.xml
index 0a0dc36..851c0c2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4019,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" />
@@ -5193,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/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/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/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/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/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/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/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index fcb1bfe..4d020c5 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -18,9 +18,9 @@
#include <SkImage.h>
#include <include/gpu/ganesh/SkImageGanesh.h>
-#include <include/gpu/GrBackendSurfaceMutableState.h>
#include <include/gpu/GrDirectContext.h>
#include <include/gpu/GrBackendSurface.h>
+#include <include/gpu/MutableTextureState.h>
#include "renderthread/RenderThread.h"
#include "utils/Color.h"
#include "utils/PaintUtils.h"
@@ -142,8 +142,8 @@
LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
if (mBackendTexture.isValid()) {
// Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
- GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED,
- VK_QUEUE_FAMILY_FOREIGN_EXT);
+ skgpu::MutableTextureState newState(VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_QUEUE_FAMILY_FOREIGN_EXT);
// The unref for this ref happens in the releaseProc passed into setBackendTextureState. The
// releaseProc callback will be made when the work to set the new state has finished on the
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..7744786 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,6 +17,8 @@
#include "SkiaOpenGLPipeline.h"
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/gl/GrGLTypes.h>
#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
@@ -139,7 +141,8 @@
LOG_ALWAYS_FATAL("Unsupported color type.");
}
- GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
+ auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0,
+ STENCIL_BUFFER_SIZE, fboInfo);
SkSurfaceProps props(mColorMode == ColorMode::Default ? 0 : SkSurfaceProps::kAlwaysDither_Flag,
kUnknown_SkPixelGeometry);
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/AudioManager.java b/media/java/android/media/AudioManager.java
index e8c9d0d..c3087bc 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6915,7 +6915,10 @@
/**
* @hide
- * Returns whether CSD is enabled and supported by the HAL on this device.
+ * Returns whether CSD is enabled and supported by the current active audio module HAL.
+ * This method will return {@code false) for setups in which CSD as a feature is available
+ * (see {@link AudioManager#isCsdAsAFeatureAvailable()}) and not enabled (see
+ * {@link AudioManager#isCsdAsAFeatureEnabled()}).
*/
@TestApi
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
@@ -6929,6 +6932,49 @@
/**
* @hide
+ * Returns whether CSD as a feature can be manipulated by a client. This method
+ * returns {@code true} in countries where there isn't a safe hearing regulation
+ * enforced.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean isCsdAsAFeatureAvailable() {
+ try {
+ return getService().isCsdAsAFeatureAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns {@code true} if the client has enabled CSD. This function should only
+ * be called if {@link AudioManager#isCsdAsAFeatureAvailable()} returns {@code true}.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean isCsdAsAFeatureEnabled() {
+ try {
+ return getService().isCsdAsAFeatureEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Enables/disables the CSD feature. This function should only be called if
+ * {@link AudioManager#isCsdAsAFeatureAvailable()} returns {@code true}.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setCsdAsAFeatureEnabled(boolean csdToggleValue) {
+ try {
+ getService().setCsdAsAFeatureEnabled(csdToggleValue);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Describes an audio device that has not been categorized with a specific
* audio type.
*/
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index b2466e9..5cbb4e5 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -325,6 +325,15 @@
boolean isCsdEnabled();
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean isCsdAsAFeatureAvailable();
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean isCsdAsAFeatureEnabled();
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ oneway void setCsdAsAFeatureEnabled(boolean csdToggleValue);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
oneway void setBluetoothAudioDeviceCategory(in String address, boolean isBle, int deviceType);
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
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/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/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 58106c0..bb67bbc3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -441,7 +441,7 @@
String.valueOf(Global.Wearable.TETHERED_CONFIG_TETHERED)
}));
VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_SUPPORTED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR);
}
}
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..4678559 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -723,7 +723,8 @@
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
- Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user
+ Settings.Secure.AUDIO_DEVICE_INVENTORY, // not controllable by user
+ Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED, // not controllable by user
Settings.Secure.BACKUP_AUTO_RESTORE,
Settings.Secure.BACKUP_ENABLED,
Settings.Secure.BACKUP_PROVISIONED,
@@ -859,7 +860,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/res/drawable-television/ic_volume_media.xml b/packages/SystemUI/res/drawable-television/ic_volume_media.xml
deleted file mode 100644
index 6a368d5..0000000
--- a/packages/SystemUI/res/drawable-television/ic_volume_media.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/tv_volume_icons_size"
- android:height="@dimen/tv_volume_icons_size"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
deleted file mode 100644
index 6eb944f..0000000
--- a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/tv_volume_icons_size"
- android:height="@dimen/tv_volume_icons_size"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <group android:translateX="-2">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
deleted file mode 100644
index b683089..0000000
--- a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/tv_volume_icons_size"
- android:height="@dimen/tv_volume_icons_size"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <group android:translateX="-4">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M14,8.83v6.34L11.83,13H9v-2h2.83L14,8.83M16,4l-5,5H7v6h4l5,5V4z"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml
deleted file mode 100644
index 7a44aa6..0000000
--- a/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/tv_volume_icons_size"
- android:height="@dimen/tv_volume_icons_size"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M4.34,2.93L2.93,4.34 7.29,8.7 7,9L3,9v6h4l5,5v-6.59l4.18,4.18c-0.65,0.49 -1.38,0.88 -2.18,1.11v2.06c1.34,-0.3 2.57,-0.92 3.61,-1.75l2.05,2.05 1.41,-1.41L4.34,2.93zM10,15.17L7.83,13L5,13v-2h2.83l0.88,-0.88L10,11.41v3.76zM19,12c0,0.82 -0.15,1.61 -0.41,2.34l1.53,1.53c0.56,-1.17 0.88,-2.48 0.88,-3.87 0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM12,4l-1.88,1.88L12,7.76zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v1.79l2.48,2.48c0.01,-0.08 0.02,-0.16 0.02,-0.24z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml b/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml
deleted file mode 100644
index e49fc15..0000000
--- a/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack">
- <item android:id="@android:id/background"
- android:gravity="center_vertical|fill_horizontal">
- <layer-list>
- <item android:id="@+id/volume_seekbar_background_solid">
- <shape>
- <size android:height="@dimen/volume_dialog_slider_width" />
- <solid android:color="@color/tv_volume_dialog_seek_bar_background"/>
- <corners android:radius="@dimen/volume_dialog_slider_corner_radius" />
- </shape>
- </item>
- </layer-list>
- </item>
- <item android:id="@android:id/progress"
- android:gravity="center_vertical|fill_horizontal">
- <com.android.systemui.util.RoundedCornerProgressDrawable
- android:drawable="@drawable/volume_row_seekbar_progress"
- />
- </item>
-</layer-list>
diff --git a/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml
deleted file mode 100644
index bce193a..0000000
--- a/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 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.
- -->
-
-<!-- Progress drawable for volume row SeekBars. This is the accent-colored round rect that moves up
- and down as the progress value changes. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
- <item android:id="@+id/volume_seekbar_progress_solid">
- <shape android:shape="rectangle">
- <size android:height="@dimen/volume_dialog_slider_width"/>
- <solid android:color="@color/tv_volume_dialog_seek_bar_fill" />
- <corners android:radius="@dimen/volume_dialog_slider_width" />
- </shape>
- </item>
-</layer-list>
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/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/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/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 1aa1c59..eec16e6 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -102,7 +102,8 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+ AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
};
protected static final int[] OPS_CAMERA = new int[] {
@@ -117,8 +118,7 @@
};
protected static final int[] OPS_OTHERS = new int[] {
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
+ AppOpsManager.OP_SYSTEM_ALERT_WINDOW
};
protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS);
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/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/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/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/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/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/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 c2774e5..66a77a3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -190,6 +190,8 @@
"securebox",
"apache-commons-math",
"power_optimization_flags_lib",
+ "notification_flags_lib",
+ "pm_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index fc51e2e..557e4ac 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1374,7 +1374,7 @@
* and the package should get out of stopped state and be enabled.
*/
public abstract void notifyComponentUsed(@NonNull String packageName,
- @UserIdInt int userId, @NonNull String recentCallingPackage);
+ @UserIdInt int userId, @NonNull String recentCallingPackage, @NonNull String debugInfo);
/** @deprecated For legacy shell command only. */
@Deprecated
@@ -1403,4 +1403,10 @@
*/
public abstract int[] getDistractingPackageRestrictionsAsUser(
@NonNull String[] packageNames, int userId);
+
+ /**
+ * Checks if package is quarantined for a specific user.
+ */
+ public abstract boolean isPackageQuarantined(@NonNull String packageName,
+ @UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index d398260..330742a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5309,11 +5309,12 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
- int flags = Context.BIND_AUTO_CREATE;
+ long flags = Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE;
if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) {
flags |= Context.BIND_ALLOW_INSTANT;
}
- if (!mContext.bindServiceAsUser(intent, this, flags, UserHandle.of(mAccounts.userId))) {
+ if (!mContext.bindServiceAsUser(intent, this, Context.BindServiceFlags.of(flags),
+ UserHandle.of(mAccounts.userId))) {
Log.w(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
// Perform unbind as per documentation at Context.bindServiceAsUser
mContext.unbindService(this);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index cb246f6..93fe0c9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3601,6 +3601,8 @@
|| (flags & Context.BIND_EXTERNAL_SERVICE_LONG) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0;
+ final boolean filterOutQuarantined =
+ (flags & Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS) != 0;
ProcessRecord attributedApp = null;
if (sdkSandboxClientAppUid > 0) {
@@ -3610,7 +3612,7 @@
isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
isBindExternal, allowInstant, null /* fgsDelegateOptions */,
- inSharedIsolatedProcess);
+ inSharedIsolatedProcess, filterOutQuarantined);
if (res == null) {
return 0;
}
@@ -4119,6 +4121,20 @@
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
boolean inSharedIsolatedProcess) {
+ return retrieveServiceLocked(service, instanceName, isSdkSandboxService,
+ sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage,
+ callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal,
+ allowInstant, fgsDelegateOptions, inSharedIsolatedProcess,
+ false /* filterOutQuarantined */);
+ }
+
+ private ServiceLookupResult retrieveServiceLocked(Intent service,
+ String instanceName, boolean isSdkSandboxService, int sdkSandboxClientAppUid,
+ String sdkSandboxClientAppPackage, String resolvedType,
+ String callingPackage, int callingPid, int callingUid, int userId,
+ boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
+ boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
+ boolean inSharedIsolatedProcess, boolean filterOutQuarantined) {
if (isSdkSandboxService && instanceName == null) {
throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
}
@@ -4235,11 +4251,14 @@
if (r == null) {
try {
- int flags = ActivityManagerService.STOCK_PM_FLAGS
+ long flags = ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
if (allowInstant) {
flags |= PackageManager.MATCH_INSTANT;
}
+ if (filterOutQuarantined) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
// TODO: come back and remove this assumption to triage all services
ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
resolvedType, flags, userId, callingUid);
@@ -5118,7 +5137,7 @@
try {
mAm.mPackageManagerInt.notifyComponentUsed(
- r.packageName, r.userId, r.mRecentCallingPackage);
+ r.packageName, r.userId, r.mRecentCallingPackage, r.toString());
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a53c2fb..c4816fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -58,6 +58,7 @@
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
+import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -7409,6 +7410,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: "
@@ -14218,7 +14222,8 @@
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users, int[] broadcastAllowList) {
// TODO: come back and remove this assumption to triage all broadcasts
- int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
+ long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING
+ | FILTER_OUT_QUARANTINED_COMPONENTS;
List<ResolveInfo> receivers = null;
HashSet<ComponentName> singleUserReceivers = null;
@@ -14846,6 +14851,16 @@
mAtmInternal.onPackagesSuspendedChanged(packageNames, suspended,
userIdExtra);
+
+ final boolean quarantined = intent.getBooleanExtra(
+ Intent.EXTRA_QUARANTINED, false);
+ if (suspended && quarantined && packageNames != null) {
+ for (int i = 0; i < packageNames.length; i++) {
+ forceStopPackageLocked(packageNames[i], -1, false, true, true,
+ false, false, userId, "suspended");
+ }
+ }
+
break;
}
break;
diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java
index 6e93ce4..3fa6102 100644
--- a/services/core/java/com/android/server/am/ComponentAliasResolver.java
+++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java
@@ -457,7 +457,7 @@
@Nullable
public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent,
@NonNull ResolveInfo receiver, @Nullable String resolvedType,
- int packageFlags, int userId, int callingUid, boolean forSend) {
+ long packageFlags, int userId, int callingUid, boolean forSend) {
// Resolve this alias.
final Resolution<ComponentName> resolution = resolveComponentAlias(() ->
receiver.activityInfo.getComponentName());
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 65fd54a..4cc147f 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -509,7 +509,8 @@
checkTime(startTime,
"getContentProviderImpl: before set stopped state");
mService.mPackageManagerInt.notifyComponentUsed(
- cpr.appInfo.packageName, userId, callingPackage);
+ cpr.appInfo.packageName, userId, callingPackage,
+ cpr.toString());
checkTime(startTime, "getContentProviderImpl: after set stopped state");
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8a8e2af..70a1c91 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1531,7 +1531,11 @@
sb.append("ServiceRecord{")
.append(Integer.toHexString(System.identityHashCode(this)))
.append(" u").append(userId)
- .append(' ').append(shortInstanceName).append('}');
+ .append(' ').append(shortInstanceName);
+ if (mRecentCallingPackage != null) {
+ sb.append(" c:").append(mRecentCallingPackage);
+ }
+ sb.append('}');
return stringName = sb.toString();
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 86da39b..a17b3d5 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -97,7 +97,7 @@
*/
public final class AppHibernationService extends SystemService {
private static final String TAG = "AppHibernationService";
- private static final int PACKAGE_MATCH_FLAGS =
+ private static final long PACKAGE_MATCH_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 76c4cfe..3f50dfd3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -10742,6 +10742,27 @@
@Override
@android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean isCsdAsAFeatureAvailable() {
+ super.isCsdAsAFeatureAvailable_enforcePermission();
+ return mSoundDoseHelper.isCsdAsAFeatureAvailable();
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public boolean isCsdAsAFeatureEnabled() {
+ super.isCsdAsAFeatureEnabled_enforcePermission();
+ return mSoundDoseHelper.isCsdAsAFeatureEnabled();
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setCsdAsAFeatureEnabled(boolean csdToggleValue) {
+ super.setCsdAsAFeatureEnabled_enforcePermission();
+ mSoundDoseHelper.setCsdAsAFeatureEnabled(csdToggleValue);
+ }
+
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle,
@AudioDeviceCategory int btAudioDeviceCategory) {
super.setBluetoothAudioDeviceCategory_enforcePermission();
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 1578193..95b6c2c 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -37,13 +37,11 @@
import android.media.ISoundDoseCallback;
import android.media.SoundDoseRecord;
import android.os.Binder;
-import android.os.HandlerExecutor;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -143,8 +141,6 @@
private static final int SAFE_MEDIA_VOLUME_UNINITIALIZED = -1;
- private static final String FEATURE_FLAG_ENABLE_CSD = "enable_csd";
-
private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE,
"CSD updates");
@@ -193,7 +189,15 @@
private final AtomicBoolean mEnableCsd = new AtomicBoolean(false);
- private ArrayList<ISoundDose.AudioDeviceCategory> mCachedAudioDeviceCategories =
+ private final Object mCsdAsAFeatureLock = new Object();
+
+ @GuardedBy("mCsdAsAFeatureLock")
+ private boolean mIsCsdAsAFeatureAvailable = false;
+
+ @GuardedBy("mCsdAsAFeatureLock")
+ private boolean mIsCsdAsAFeatureEnabled = false;
+
+ private final ArrayList<ISoundDose.AudioDeviceCategory> mCachedAudioDeviceCategories =
new ArrayList<>();
private final Object mCsdStateLock = new Object();
@@ -315,10 +319,6 @@
mAlarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
-
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_MEDIA,
- new HandlerExecutor(mAudioHandler),
- p -> updateCsdEnabled("onPropertiesChanged"));
}
void initSafeVolumes() {
@@ -493,6 +493,38 @@
return false;
}
+ boolean isCsdAsAFeatureAvailable() {
+ synchronized (mCsdAsAFeatureLock) {
+ return mIsCsdAsAFeatureAvailable;
+ }
+ }
+
+ boolean isCsdAsAFeatureEnabled() {
+ synchronized (mCsdAsAFeatureLock) {
+ return mIsCsdAsAFeatureEnabled;
+ }
+ }
+
+ void setCsdAsAFeatureEnabled(boolean csdAsAFeatureEnabled) {
+ boolean doUpdate;
+ synchronized (mCsdAsAFeatureLock) {
+ doUpdate = mIsCsdAsAFeatureEnabled != csdAsAFeatureEnabled && mIsCsdAsAFeatureAvailable;
+ mIsCsdAsAFeatureEnabled = csdAsAFeatureEnabled;
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mSettings.putSecureIntForUser(mAudioService.getContentResolver(),
+ Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED,
+ mIsCsdAsAFeatureEnabled ? 1 : 0, UserHandle.USER_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ if (doUpdate) {
+ updateCsdEnabled("setCsdAsAFeatureEnabled");
+ }
+ }
+
void setAudioDeviceCategory(String address, int internalAudioType, boolean isHeadphone) {
if (!mEnableCsd.get()) {
return;
@@ -863,6 +895,13 @@
Log.e(TAG, "Exception while forcing the internal MEL computation", e);
}
+ synchronized (mCsdAsAFeatureLock) {
+ mIsCsdAsAFeatureEnabled = mSettings.getSecureIntForUser(
+ mAudioService.getContentResolver(),
+ Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED, 0,
+ UserHandle.USER_CURRENT) != 0;
+ }
+
synchronized (mCsdStateLock) {
if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
@@ -907,18 +946,23 @@
@GuardedBy("mSafeMediaVolumeStateLock")
private void updateSafeMediaVolume_l(String caller) {
- boolean safeMediaVolumeEnabled =
- SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_FORCE, false)
- || (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_safe_media_volume_enabled)
- && !mEnableCsd.get());
boolean safeMediaVolumeBypass =
- SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_BYPASS, false);
+ SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_BYPASS, false)
+ || mEnableCsd.get();
+ boolean safeMediaVolumeForce = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_FORCE,
+ false);
+ // we are using the MCC overlaid legacy flag used for the safe volume enablement
+ // to determine whether the MCC enforces any safe hearing standard.
+ boolean mccEnforcedSafeMediaVolume = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_safe_media_volume_enabled);
+
+ boolean safeVolumeEnabled =
+ (mccEnforcedSafeMediaVolume || safeMediaVolumeForce) && !safeMediaVolumeBypass;
// The persisted state is either "disabled" or "active": this is the state applied
// next time we boot and cannot be "inactive"
int persistedState;
- if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) {
+ if (safeVolumeEnabled) {
persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
// The state can already be "inactive" here if the user has forced it before
// the 30 seconds timeout for forced configuration. In this case we don't reset
@@ -945,22 +989,28 @@
}
private void updateCsdEnabled(String caller) {
- boolean newEnableCsd = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE,
- false);
- if (!newEnableCsd) {
- final String featureFlagEnableCsdValue = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_MEDIA,
- FEATURE_FLAG_ENABLE_CSD);
- if (featureFlagEnableCsdValue != null) {
- newEnableCsd = Boolean.parseBoolean(featureFlagEnableCsdValue);
+ boolean csdForce = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE, false);
+ // we are using the MCC overlaid legacy flag used for the safe volume enablement
+ // to determine whether the MCC enforces any safe hearing standard.
+ boolean mccEnforcedSafeMedia = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_safe_media_volume_enabled);
+ boolean csdEnable = mContext.getResources().getBoolean(
+ R.bool.config_safe_sound_dosage_enabled);
+ boolean newEnabledCsd = (mccEnforcedSafeMedia && csdEnable) || csdForce;
+
+ synchronized (mCsdAsAFeatureLock) {
+ if (!mccEnforcedSafeMedia && csdEnable) {
+ mIsCsdAsAFeatureAvailable = true;
+ newEnabledCsd = mIsCsdAsAFeatureEnabled || csdForce;
+ Log.v(TAG, caller + ": CSD as a feature is not enforced and enabled: "
+ + newEnabledCsd);
} else {
- newEnableCsd = mContext.getResources().getBoolean(
- R.bool.config_safe_sound_dosage_enabled);
+ mIsCsdAsAFeatureAvailable = false;
}
}
- if (mEnableCsd.compareAndSet(!newEnableCsd, newEnableCsd)) {
- Log.i(TAG, caller + ": enable CSD " + newEnableCsd);
+ if (mEnableCsd.compareAndSet(!newEnabledCsd, newEnabledCsd)) {
+ Log.i(TAG, caller + ": enabled CSD " + newEnabledCsd);
initCsd();
synchronized (mSafeMediaVolumeStateLock) {
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/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 3e31bd1..0aac7c2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -220,8 +220,10 @@
private static final int SYNC_OP_STATE_INVALID_SYNC_DISABLED = 5;
/** Flags used when connecting to a sync adapter service */
- private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT;
+ private static final Context.BindServiceFlags SYNC_ADAPTER_CONNECTION_FLAGS =
+ Context.BindServiceFlags.of(
+ Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT);
/** Singleton instance. */
@GuardedBy("SyncManager.class")
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/Android.bp b/services/core/java/com/android/server/pm/Android.bp
new file mode 100644
index 0000000..89c0124
--- /dev/null
+++ b/services/core/java/com/android/server/pm/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+ name: "pm_flags",
+ package: "com.android.server.pm",
+ srcs: [
+ "*.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "pm_flags_lib",
+ aconfig_declarations: "pm_flags",
+}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 5b05b48..7d878ec 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -522,6 +522,7 @@
comp != null || pkgName != null /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType,
flags));
+
List<ResolveInfo> list = Collections.emptyList();
boolean skipPostResolution = false;
if (comp != null) {
@@ -643,6 +644,12 @@
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
+
+ // Only if the query is coming from the system process,
+ // it should be allowed to match quarantined components
+ if (callingUid != Process.SYSTEM_UID) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -4031,6 +4038,9 @@
flags = updateFlagsForComponent(flags, userId);
enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
false /* checkShell */, "get provider info");
+ if (callingUid != Process.SYSTEM_UID) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
ParsedProvider p = mComponentResolver.getProvider(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
@@ -4660,6 +4670,9 @@
int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
+ if (callingUid != Process.SYSTEM_UID) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
userId);
boolean checkedGrants = false;
@@ -4772,6 +4785,13 @@
false /* checkShell */, "queryContentProviders");
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId);
+
+ // Only if the service query is coming from the system process,
+ // it should be allowed to match quarantined components
+ if (callingUid != Process.SYSTEM_UID) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
+
ArrayList<ProviderInfo> finalList = null;
final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
metaDataKey, uid, flags, userId);
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/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 82587c5..6efd067 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -746,10 +746,18 @@
}
@Override
- public void notifyComponentUsed(@NonNull String packageName,
- @UserIdInt int userId, @NonNull String recentCallingPackage) {
+ public void notifyComponentUsed(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull String recentCallingPackage, @NonNull String debugInfo) {
mService.notifyComponentUsed(snapshot(), packageName, userId,
- recentCallingPackage);
+ recentCallingPackage, debugInfo);
+ }
+
+ @Override
+ public boolean isPackageQuarantined(@NonNull String packageName,
+ @UserIdInt int userId) {
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ return (packageState == null) ? false
+ : packageState.getUserStateOrDefault(userId).isQuarantined();
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6bcfcfe..6c0aeec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4583,7 +4583,18 @@
}
void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName,
- @UserIdInt int userId, @NonNull String recentCallingPackage) {
+ @UserIdInt int userId, @NonNull String recentCallingPackage,
+ @NonNull String debugInfo) {
+ synchronized (mLock) {
+ final PackageUserStateInternal userState = mSettings.getPackageLPr(
+ packageName).getUserStateOrDefault(userId);
+ if (userState.isQuarantined()) {
+ Slog.i(TAG,
+ "Component is quarantined+suspended but being used: "
+ + packageName + " by " + recentCallingPackage + ", debugInfo: "
+ + debugInfo);
+ }
+ }
PackageManagerService.this
.setPackageStoppedState(snapshot, packageName, false /* stopped */,
userId);
@@ -6120,14 +6131,16 @@
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
- SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
+ SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshotComputer();
enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
"setPackagesSuspendedAsUser");
+ boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0)
+ && Flags.quarantinedEnabled();
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
- false /* forQuietMode */);
+ false /* forQuietMode */, quarantined);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ce0e7ad..ceae1fe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -288,9 +288,11 @@
case "unhide":
return runSetHiddenSetting(false);
case "suspend":
- return runSuspend(true);
+ return runSuspend(true, 0);
+ case "suspend-quarantine":
+ return runSuspend(true, PackageManager.FLAG_SUSPEND_QUARANTINED);
case "unsuspend":
- return runSuspend(false);
+ return runSuspend(false, 0);
case "set-distracting-restriction":
return runSetDistractingRestriction();
case "get-distracting-restriction":
@@ -2644,7 +2646,7 @@
}
}
- private int runSuspend(boolean suspendedState) {
+ private int runSuspend(boolean suspendedState, int flags) {
final PrintWriter pw = getOutPrintWriter();
int userId = UserHandle.USER_SYSTEM;
String dialogMessage = null;
@@ -2712,7 +2714,7 @@
mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}),
suspendedState, ((appExtras.size() > 0) ? appExtras : null),
((launcherExtras.size() > 0) ? launcherExtras : null),
- info, callingPackage, translatedUserId);
+ info, flags, callingPackage, translatedUserId);
for (int i = 0; i < packageNames.size(); i++) {
final String packageName = packageNames.get(i);
pw.println("Package " + packageName + " new suspended state: "
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index dee31ec..14693a6 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -937,8 +937,8 @@
otherState.isHidden(), otherState.getDistractionFlags(),
otherState.getSuspendParams() == null
? null : otherState.getSuspendParams().untrackedStorage(),
- otherState.isInstantApp(),
- otherState.isVirtualPreload(), otherState.getLastDisableAppCaller(),
+ otherState.isInstantApp(), otherState.isVirtualPreload(),
+ otherState.getLastDisableAppCaller(),
otherState.getEnabledComponentsNoCopy() == null
? null : otherState.getEnabledComponentsNoCopy().untrackedStorage(),
otherState.getDisabledComponentsNoCopy() == null
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c6135e0..b6da462 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1918,9 +1918,9 @@
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
- PersistableBundle suspendedAppExtras = null;
- PersistableBundle suspendedLauncherExtras = null;
SuspendDialogInfo oldSuspendDialogInfo = null;
+ PersistableBundle oldSuspendedAppExtras = null;
+ PersistableBundle oldSuspendedLauncherExtras = null;
ArchiveState archiveState = null;
int packageDepth = parser.getDepth();
@@ -1939,16 +1939,17 @@
case TAG_DISABLED_COMPONENTS:
disabledComponents = readComponentsLPr(parser);
break;
- case TAG_SUSPENDED_APP_EXTRAS:
- suspendedAppExtras = PersistableBundle.restoreFromXml(parser);
- break;
- case TAG_SUSPENDED_LAUNCHER_EXTRAS:
- suspendedLauncherExtras = PersistableBundle.restoreFromXml(
- parser);
- break;
case TAG_SUSPENDED_DIALOG_INFO:
oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
break;
+ case TAG_SUSPENDED_APP_EXTRAS:
+ oldSuspendedAppExtras = PersistableBundle.restoreFromXml(
+ parser);
+ break;
+ case TAG_SUSPENDED_LAUNCHER_EXTRAS:
+ oldSuspendedLauncherExtras = PersistableBundle.restoreFromXml(
+ parser);
+ break;
case TAG_SUSPEND_PARAMS:
final String suspendingPackage = parser.getAttributeValue(null,
ATTR_SUSPENDING_PACKAGE);
@@ -1979,8 +1980,9 @@
if (suspended && suspendParamsMap == null) {
final SuspendParams suspendParams = new SuspendParams(
oldSuspendDialogInfo,
- suspendedAppExtras,
- suspendedLauncherExtras);
+ oldSuspendedAppExtras,
+ oldSuspendedLauncherExtras,
+ false /* quarantined */);
suspendParamsMap = new ArrayMap<>();
suspendParamsMap.put(oldSuspendingPackage, suspendParams);
}
@@ -1989,13 +1991,12 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped,
- notLaunched,
- hidden, distractionFlags, suspendParamsMap, instantApp,
- virtualPreload,
- enabledCaller, enabledComponents, disabledComponents, installReason,
- uninstallReason, harmfulAppWarning, splashScreenTheme,
- firstInstallTime != 0 ? firstInstallTime :
- origFirstInstallTimes.getOrDefault(name, 0L),
+ notLaunched, hidden, distractionFlags, suspendParamsMap, instantApp,
+ virtualPreload, enabledCaller, enabledComponents,
+ disabledComponents, installReason, uninstallReason,
+ harmfulAppWarning, splashScreenTheme,
+ firstInstallTime != 0 ? firstInstallTime
+ : origFirstInstallTimes.getOrDefault(name, 0L),
minAspectRatio, archiveState);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
@@ -4824,6 +4825,7 @@
pw.print(userState.isNotLaunched() ? "l" : "L");
pw.print(userState.isInstantApp() ? "IA" : "ia");
pw.print(userState.isVirtualPreload() ? "VPI" : "vpi");
+ pw.print(userState.isQuarantined() ? "Q" : "q");
String harmfulAppWarning = userState.getHarmfulAppWarning();
pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
@@ -5185,6 +5187,8 @@
pw.print(userState.isInstantApp());
pw.print(" virtual=");
pw.println(userState.isVirtualPreload());
+ pw.print(" quarantined=");
+ pw.print(userState.isQuarantined());
pw.print(" installReason=");
pw.println(userState.getInstallReason());
@@ -5207,6 +5211,8 @@
if (params != null) {
pw.print(" dialogInfo=");
pw.print(params.getDialogInfo());
+ pw.print(" quarantined=");
+ pw.println(params.isQuarantined());
}
pw.println();
}
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 94e09f1..ddb045d 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -115,17 +115,18 @@
boolean suspended, @Nullable PersistableBundle appExtras,
@Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
@NonNull String callingPackage, @UserIdInt int userId, int callingUid,
- boolean forQuietMode) {
+ boolean forQuietMode, boolean quarantined) {
if (ArrayUtils.isEmpty(packageNames)) {
return packageNames;
}
- if (suspended && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId, callingUid)) {
+ if (suspended && !quarantined && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId,
+ callingUid)) {
Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
return packageNames;
}
final SuspendParams newSuspendParams =
- new SuspendParams(dialogInfo, appExtras, launcherExtras);
+ new SuspendParams(dialogInfo, appExtras, launcherExtras, quarantined);
final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
@@ -160,7 +161,6 @@
final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
packageState.getUserStateOrDefault(userId).getSuspendParams();
-
SuspendParams oldSuspendParams = suspendParamsMap == null
? null : suspendParamsMap.get(packageName);
boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
@@ -213,14 +213,15 @@
sendPackagesSuspendedForUser(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
- changedPackages, notifyUids.toArray(), userId);
+ changedPackages, notifyUids.toArray(), quarantined, userId);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
mPm.scheduleWritePackageRestrictions(userId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
- changedPackagesList.toArray(new String[0]), changedUids.toArray(), userId);
+ changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
+ userId);
}
return unmodifiablePackages.toArray(new String[0]);
}
@@ -354,7 +355,7 @@
new String[unsuspendedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
- packageArray, unsuspendedUids.toArray(), userId);
+ packageArray, unsuspendedUids.toArray(), false, userId);
}
}
@@ -618,11 +619,14 @@
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
- @NonNull int[] uidList, int userId) {
+ @NonNull int[] uidList, boolean quarantined, int userId) {
final Handler handler = mInjector.getHandler();
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ if (quarantined) {
+ extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
+ }
final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
final Bundle options = new BroadcastOptions()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
@@ -672,7 +676,7 @@
snapshot, toSuspend.toArray(new String[0]), suspend,
null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
- false /* forQuietMode */)));
+ false /* forQuietMode */, false /* quarantined */)));
}
return unsuspendable.toArray(String[]::new);
}
@@ -716,7 +720,7 @@
setPackagesSuspended(snapshot, toSuspend.toArray(new String[0]),
suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
- true /* forQuietMode */);
+ true /* forQuietMode */, false /* quarantined */);
}
private Set<String> packagesToSuspendInQuietMode(Computer snapshot, int userId) {
diff --git a/services/core/java/com/android/server/pm/flags.aconfig b/services/core/java/com/android/server/pm/flags.aconfig
new file mode 100644
index 0000000..368a843
--- /dev/null
+++ b/services/core/java/com/android/server/pm/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.pm"
+
+flag {
+ name: "quarantined_enabled"
+ namespace: "package_manager_service"
+ description: "Feature flag for Quarantined state"
+ bug: "269127435"
+}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 94e9599..27812df 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -379,7 +379,10 @@
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
- if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0
+ && state.isQuarantined()) {
+ ai.enabled = false;
+ } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 81915b4..7bc518c 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -209,6 +209,13 @@
boolean isVirtualPreload();
/**
+ * @return whether the package is quarantined in order to minimize ad-spam and pop ups
+ * when-not-in-use.
+ * @hide
+ */
+ boolean isQuarantined();
+
+ /**
* The "package:type/entry" form of the theme resource ID previously set as the splash screen.
*
* @hide
@@ -225,6 +232,7 @@
*/
@PackageManager.UserMinAspectRatio
int getMinAspectRatio();
+
/**
* Information about the archived state of an app. Set only if an app is archived.
*
@@ -233,4 +241,5 @@
@Immutable.Ignore
@Nullable
ArchiveState getArchiveState();
+
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index cce18a8..3534d75 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -129,6 +129,11 @@
return false;
}
+ @Override
+ public boolean isQuarantined() {
+ return false;
+ }
+
@Nullable
@Override
public String getSplashScreenTheme() {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index d8c8af6..2349fbf 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -670,6 +670,21 @@
return getBoolean(Booleans.VIRTUAL_PRELOADED);
}
+ @Override
+ public boolean isQuarantined() {
+ if (!isSuspended()) {
+ return false;
+ }
+ final var suspendParams = mSuspendParams;
+ for (int i = 0, size = suspendParams.size(); i < size; i++) {
+ final SuspendParams params = suspendParams.valueAt(i);
+ if (params.isQuarantined()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@@ -879,7 +894,7 @@
}
@DataClass.Generated(
- time = 1691186062924L,
+ time = 1691601685901L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index 15e3d0c..e342453 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -16,6 +16,7 @@
package com.android.server.pm.pkg;
+import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
@@ -122,6 +123,10 @@
return true;
}
+ if ((flags & FILTER_OUT_QUARANTINED_COMPONENTS) != 0 && state.isQuarantined()) {
+ return false;
+ }
+
// First check if the overall package is disabled; if the package is
// enabled then fall through to check specific component
switch (state.getEnabledState()) {
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index dc48a33..153238fa 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -42,16 +42,25 @@
private static final String TAG_DIALOG_INFO = "dialog-info";
private static final String TAG_APP_EXTRAS = "app-extras";
private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
+ private static final String ATTR_QUARANTINED = "quarantined";
- private final SuspendDialogInfo dialogInfo;
- private final PersistableBundle appExtras;
- private final PersistableBundle launcherExtras;
+ private final SuspendDialogInfo mDialogInfo;
+ private final PersistableBundle mAppExtras;
+ private final PersistableBundle mLauncherExtras;
+
+ private final boolean mQuarantined;
public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
PersistableBundle launcherExtras) {
- this.dialogInfo = dialogInfo;
- this.appExtras = appExtras;
- this.launcherExtras = launcherExtras;
+ this(dialogInfo, appExtras, launcherExtras, false /* quarantined */);
+ }
+
+ public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+ PersistableBundle launcherExtras, boolean quarantined) {
+ this.mDialogInfo = dialogInfo;
+ this.mAppExtras = appExtras;
+ this.mLauncherExtras = launcherExtras;
+ this.mQuarantined = quarantined;
}
@Override
@@ -63,13 +72,16 @@
return false;
}
final SuspendParams other = (SuspendParams) obj;
- if (!Objects.equals(dialogInfo, other.dialogInfo)) {
+ if (!Objects.equals(mDialogInfo, other.mDialogInfo)) {
return false;
}
- if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) {
+ if (!BaseBundle.kindofEquals(mAppExtras, other.mAppExtras)) {
return false;
}
- if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) {
+ if (!BaseBundle.kindofEquals(mLauncherExtras, other.mLauncherExtras)) {
+ return false;
+ }
+ if (mQuarantined != other.mQuarantined) {
return false;
}
return true;
@@ -77,9 +89,10 @@
@Override
public int hashCode() {
- int hashCode = Objects.hashCode(dialogInfo);
- hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0);
- hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0);
+ int hashCode = Objects.hashCode(mDialogInfo);
+ hashCode = 31 * hashCode + ((mAppExtras != null) ? mAppExtras.size() : 0);
+ hashCode = 31 * hashCode + ((mLauncherExtras != null) ? mLauncherExtras.size() : 0);
+ hashCode = 31 * hashCode + Boolean.hashCode(mQuarantined);
return hashCode;
}
@@ -89,25 +102,26 @@
* @param out the {@link XmlSerializer} object
*/
public void saveToXml(TypedXmlSerializer out) throws IOException {
- if (dialogInfo != null) {
+ out.attributeBoolean(null, ATTR_QUARANTINED, mQuarantined);
+ if (mDialogInfo != null) {
out.startTag(null, TAG_DIALOG_INFO);
- dialogInfo.saveToXml(out);
+ mDialogInfo.saveToXml(out);
out.endTag(null, TAG_DIALOG_INFO);
}
- if (appExtras != null) {
+ if (mAppExtras != null) {
out.startTag(null, TAG_APP_EXTRAS);
try {
- appExtras.saveToXml(out);
+ mAppExtras.saveToXml(out);
} catch (XmlPullParserException e) {
Slog.e(LOG_TAG, "Exception while trying to write appExtras."
+ " Will be lost on reboot", e);
}
out.endTag(null, TAG_APP_EXTRAS);
}
- if (launcherExtras != null) {
+ if (mLauncherExtras != null) {
out.startTag(null, TAG_LAUNCHER_EXTRAS);
try {
- launcherExtras.saveToXml(out);
+ mLauncherExtras.saveToXml(out);
} catch (XmlPullParserException e) {
Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
+ " Will be lost on reboot", e);
@@ -127,6 +141,8 @@
PersistableBundle readAppExtras = null;
PersistableBundle readLauncherExtras = null;
+ final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false);
+
final int currentDepth = in.getDepth();
int type;
try {
@@ -157,18 +173,22 @@
Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
+ " some fields may default", e);
}
- return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras);
+ return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras, quarantined);
}
public SuspendDialogInfo getDialogInfo() {
- return dialogInfo;
+ return mDialogInfo;
}
public PersistableBundle getAppExtras() {
- return appExtras;
+ return mAppExtras;
}
public PersistableBundle getLauncherExtras() {
- return launcherExtras;
+ return mLauncherExtras;
+ }
+
+ public boolean isQuarantined() {
+ return mQuarantined;
}
}
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/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index d5fd017..f4b2f52 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -386,7 +386,11 @@
}
}
- return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS);
+ try {
+ return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS);
+ } catch (RejectedExecutionException e) {
+ return CompletableFuture.failedFuture(e);
+ }
}
public synchronized Future<?> scheduleWrite() {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d8cc8d3..57b6e37 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1329,7 +1329,7 @@
.notifyBeforePackageUnstopped(next.packageName);
mAtmService.getPackageManagerInternalLocked().notifyComponentUsed(
next.packageName, next.mUserId,
- next.packageName); /* TODO: Verify if correct userid */
+ next.packageName, next.toString()); /* TODO: Verify if correct userid */
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ next.packageName + ": " + e);
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/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/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..af1bac8 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);
@@ -11280,25 +11004,18 @@
}
}
- private void dumpPerUserData(IndentingPrintWriter pw) {
+ private void dumpPersonalAppInfoForSystemUserNoLock(IndentingPrintWriter pw) {
+ wtfIfInLock();
+ PersonalAppsSuspensionHelper.forUser(mContext, UserHandle.USER_SYSTEM).dump(pw);
+ }
+
+ private void dumpPerUserPolicyData(IndentingPrintWriter pw) {
int userCount = mUserData.size();
for (int i = 0; i < userCount; i++) {
int userId = mUserData.keyAt(i);
DevicePolicyData policy = getUserData(userId);
policy.dump(pw);
pw.println();
-
- if (userId == UserHandle.USER_SYSTEM) {
- pw.increaseIndent();
- PersonalAppsSuspensionHelper.forUser(mContext, userId).dump(pw);
- pw.decreaseIndent();
- pw.println();
- } else {
- // pm.getUnsuspendablePackages() will fail if it's called for a different user;
- // as this dump is mostly useful for system user anyways, we can just ignore the
- // others (rather than changing the permission check in the PM method)
- Slogf.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId);
- }
}
}
@@ -11316,7 +11033,7 @@
pw.println();
mDeviceAdminServiceController.dump(pw);
pw.println();
- dumpPerUserData(pw);
+ dumpPerUserPolicyData(pw);
pw.println();
mConstants.dump(pw);
pw.println();
@@ -11343,6 +11060,7 @@
mStateCache.dump(pw);
pw.println();
}
+ dumpPersonalAppInfoForSystemUserNoLock(pw);
synchronized (mSubscriptionsChangedListenerLock) {
pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
@@ -11434,53 +11152,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 +11188,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 +11943,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 +11976,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 +12019,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 +12044,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 +12077,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 +12364,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 +12960,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 +13093,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 +13162,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 +13359,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 +13388,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 +13400,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 +13412,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 +13446,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 +13637,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 +13673,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 +13712,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 +14251,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 +14294,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 +14321,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 +14340,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 +14386,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 +14410,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 +15714,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 +17789,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 +19703,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 +19893,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 +19950,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 +20243,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 +21541,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 +21574,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 +21986,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 +22976,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 +23327,7 @@
}
private boolean shouldMigrateToDevicePolicyEngine() {
- return mInjector.binderWithCleanCallingIdentity(() ->
- (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled())
- && !mOwners.isMigratedToPolicyEngine());
+ return mInjector.binderWithCleanCallingIdentity(() -> !mOwners.isMigratedToPolicyEngine());
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 5cca5fa..6797576 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -40,7 +40,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -64,14 +64,14 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
null /* packageNames */, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
assertThat(failedNames).isNull()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOfNulls(0), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
assertThat(failedNames).isEmpty()
}
@@ -81,7 +81,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
- Binder.getCallingUid(), false /* forQuietMode */)
+ Binder.getCallingUid(), false /* forQuietMode */, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
@@ -92,7 +92,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
@@ -103,7 +103,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
@@ -116,7 +116,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
knownPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)!!
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)!!
assertThat(failedNames.size).isEqualTo(knownPackages.size)
for (pkg in knownPackages) {
@@ -132,7 +132,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
knownPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */)!!
+ TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */, false /* quarantined */)!!
assertThat(failedNames.size).isEqualTo(1)
assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE)
@@ -144,13 +144,13 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, false /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -202,7 +202,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */)
+ false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -220,7 +220,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */)
+ false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
@@ -265,7 +265,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */)
+ false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -280,7 +280,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)
+ TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -295,7 +295,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */)
+ false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -310,7 +310,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
- deviceOwnerUid, false /* forQuietMode */)
+ deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -324,7 +324,7 @@
@Throws(Exception::class)
fun sendPackagesSuspendedForUser() {
suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, TEST_USER_ID)
+ Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID)
testHandler.flush()
verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
@@ -341,7 +341,7 @@
@Throws(Exception::class)
fun sendPackagesSuspendModifiedForUser() {
suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, TEST_USER_ID)
+ Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID)
testHandler.flush()
verify(broadcastHelper).sendPackageBroadcast(
eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
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/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index b539a76..943a9c47 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -236,7 +236,7 @@
verify(getServices().packageManagerInternal, never())
.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
- any(), anyBoolean(), any(), any(), any(), any(), anyInt());
+ any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt());
final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7478778..f408ef0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -7527,7 +7527,7 @@
.cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
// Verify that the apps are NOT unsuspeded.
verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
- any(), eq(false), any(), any(), any(), any(), anyInt());
+ any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt());
// Verify that DPC is invoked to check policy compliance.
verify(mContext.spiedContext).startActivityAsUser(
MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE),
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/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index 24029b1..fc27edc 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -35,6 +35,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -68,6 +69,27 @@
mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager();
}
+ @After
+ public void tearDown() throws Exception {
+ // Changes to RoleManager persist between tests, so we need to clear out any funny
+ // business we did in previous tests.
+ RoleManager roleManager = mContext.getSystemService(RoleManager.class);
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(
+ () -> {
+ roleManager.setBypassingRoleQualification(false);
+ roleManager.removeRoleHolderAsUser(
+ "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
+ mContext.getPackageName(),
+ /* flags= */ 0,
+ Process.myUserHandle(),
+ mContext.getMainExecutor(),
+ future);
+ });
+
+ assertThat(future.get()).isEqualTo(true);
+ }
+
@Test
public void testBugreportFileManagerFileExists() {
Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
@@ -131,14 +153,17 @@
new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>()));
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
CallbackFuture future = new CallbackFuture();
- runWithShellPermissionIdentity(() -> roleManager.setBypassingRoleQualification(true));
- runWithShellPermissionIdentity(() -> roleManager.addRoleHolderAsUser(
- "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
- mContext.getPackageName(),
- /* flags= */ 0,
- Process.myUserHandle(),
- mContext.getMainExecutor(),
- future));
+ runWithShellPermissionIdentity(
+ () -> {
+ roleManager.setBypassingRoleQualification(true);
+ roleManager.addRoleHolderAsUser(
+ "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
+ mContext.getPackageName(),
+ /* flags= */ 0,
+ Process.myUserHandle(),
+ mContext.getMainExecutor(),
+ future);
+ });
assertThat(future.get()).isEqualTo(true);
mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName());
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/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 3eed0b7..302ad7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2719,6 +2719,9 @@
assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation);
+ // Unblock the rotation animation, so the further orientation updates won't be ignored.
+ unblockDisplayRotation(activity.mDisplayContent);
+
final ActivityRecord topActivity = createActivityRecord(activity.getTask());
topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
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/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/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/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 62de67a..ae7b161 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -963,6 +963,8 @@
* @see DisplayRotation#updateRotationUnchecked
*/
void unblockDisplayRotation(DisplayContent dc) {
+ dc.mOpeningApps.clear();
+ mWm.mAppsFreezingScreen = 0;
mWm.stopFreezingDisplayLocked();
// The rotation animation won't actually play, it needs to be cleared manually.
dc.setRotationAnimation(null);
@@ -971,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/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 64c2a4c..baacb57 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -85,11 +85,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -1328,10 +1328,24 @@
private final Context mContext;
- // Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing
- // the Context and subId.
- private static final Map<Pair<Context, Integer>, Resources> sResourcesCache =
- new ConcurrentHashMap<>();
+ /**
+ * In order to prevent the overflow of the heap size due to an indiscriminate increase in the
+ * cache, the heap size of the resource cache is set sufficiently large.
+ */
+ private static final int MAX_RESOURCE_CACHE_ENTRY_COUNT = 10_000;
+
+ /**
+ * Cache of Resources that has been created in getResourcesForSubId. Key contains package name,
+ * and Configuration of Resources. If more than the maximum number of resources are stored in
+ * this cache, the least recently used Resources will be removed to maintain the maximum size.
+ */
+ private static final Map<Pair<String, Configuration>, Resources> sResourcesCache =
+ Collections.synchronizedMap(new LinkedHashMap<>(16, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(Entry eldest) {
+ return size() > MAX_RESOURCE_CACHE_ENTRY_COUNT;
+ }
+ });
/**
* A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -2817,14 +2831,20 @@
@NonNull
public static Resources getResourcesForSubId(Context context, int subId,
boolean useRootLocale) {
- // Check if resources for this context and subId already exist in the resource cache.
- // Resources that use the root locale are not cached.
- Pair<Context, Integer> cacheKey = null;
- if (isValidSubscriptionId(subId) && !useRootLocale) {
- cacheKey = Pair.create(context, subId);
- if (sResourcesCache.containsKey(cacheKey)) {
+ // Check if the Resources already exists in the cache based on the given context. Find a
+ // Resource that match Configuration.
+ Pair<String, Configuration> cacheKey = null;
+ if (isValidSubscriptionId(subId)) {
+ Configuration configurationKey =
+ new Configuration(context.getResources().getConfiguration());
+ if (useRootLocale) {
+ configurationKey.setLocale(Locale.ROOT);
+ }
+ cacheKey = Pair.create(context.getPackageName(), configurationKey);
+ Resources cached = sResourcesCache.get(cacheKey);
+ if (cached != null) {
// Cache hit. Use cached Resources.
- return sResourcesCache.get(cacheKey);
+ return cached;
}
}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
index a81444d..06fc3c6 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
@@ -45,6 +45,7 @@
* Called when the satellite position changed.
*
* @param pointingInfo The pointing info containing the satellite location.
+ * Satellite location is based on magnetic north direction.
*/
void onSatellitePositionChanged(in PointingInfo pointingInfo);
}
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index 47dbdaf..dc4d38b 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -26,7 +26,7 @@
/**
* PointingInfo is used to store the position of satellite received from satellite modem.
* The position of satellite is represented by azimuth and elevation angles
- * with degrees as unit of measurement.
+ * with degrees as unit of measurement. Satellite position is based on magnetic north direction.
* @hide
*/
@SystemApi
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index dc6bdff..615990f 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -35,6 +35,8 @@
data: [
":BinaryTransparencyTestApp",
":EasterEgg",
+ ":FeatureSplitBase",
+ ":FeatureSplit1",
":com.android.apex.cts.shim.v2_rebootless_prebuilt",
],
test_suites: [
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index 346622f..6e5f08a 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -91,20 +91,20 @@
public void testCollectAllSilentInstalledMbaInfo() throws Exception {
try {
new InstallMultiple()
- .addFile("ApkVerityTestApp.apk")
- .addFile("ApkVerityTestAppSplit.apk")
+ .addFile("FeatureSplitBase.apk")
+ .addFile("FeatureSplit1.apk")
.run();
updatePreloadApp();
- assertNotNull(getDevice().getAppPackageInfo("com.android.apkverity"));
+ assertNotNull(getDevice().getAppPackageInfo("com.android.test.split.feature"));
assertNotNull(getDevice().getAppPackageInfo("com.android.egg"));
assertTrue(getDevice().setProperty("debug.transparency.bg-install-apps",
- "com.android.apkverity,com.android.egg"));
+ "com.android.test.split.feature,com.android.egg"));
runDeviceTest("testCollectAllSilentInstalledMbaInfo");
} finally {
// No need to wait until job complete, since we can't verifying very meaningfully.
cancelPendingJob();
- uninstallPackage("com.android.apkverity");
+ uninstallPackage("com.android.test.split.feature");
uninstallPackage("com.android.egg");
}
}
diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
index c087a85..2bc056e 100644
--- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
+++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
@@ -121,7 +121,7 @@
// Verify
assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side
- var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg");
+ var expectedAppNames = Set.of("com.android.test.split.feature", "com.android.egg");
var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName)
.collect(Collectors.toList());
assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames);
@@ -141,6 +141,6 @@
}
}
}
- assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit
+ assertThat(actualSplitNames).containsExactly("feature1"); // Name of FeatureSplit1
}
}
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 baf109b..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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.layoutchange
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
@@ -23,8 +23,9 @@
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
+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)
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 d97027e..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
@@ -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)
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 0fdf63c..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
@@ -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/tools/aapt2/trace/TraceBuffer.cpp b/tools/aapt2/trace/TraceBuffer.cpp
index da53739..fab2df3 100644
--- a/tools/aapt2/trace/TraceBuffer.cpp
+++ b/tools/aapt2/trace/TraceBuffer.cpp
@@ -36,13 +36,14 @@
constexpr char kEnd = 'E';
struct TracePoint {
+ char type;
pid_t tid;
int64_t time;
std::string tag;
- char type;
};
std::vector<TracePoint> traces;
+bool enabled = true;
int64_t GetTime() noexcept {
auto now = std::chrono::steady_clock::now();
@@ -51,34 +52,39 @@
} // namespace anonymous
-void AddWithTime(const std::string& tag, char type, int64_t time) noexcept {
- TracePoint t = {getpid(), time, tag, type};
- traces.emplace_back(t);
+void AddWithTime(std::string tag, char type, int64_t time) noexcept {
+ TracePoint t = {type, getpid(), time, std::move(tag)};
+ traces.emplace_back(std::move(t));
}
-void Add(const std::string& tag, char type) noexcept {
- AddWithTime(tag, type, GetTime());
+void Add(std::string tag, char type) noexcept {
+ AddWithTime(std::move(tag), type, GetTime());
}
-
-
-
void Flush(const std::string& basePath) {
- TRACE_CALL();
if (basePath.empty()) {
return;
}
+ BeginTrace(__func__); // We can't do much here, only record that it happened.
- std::stringstream s;
+ std::ostringstream s;
s << basePath << aapt::file::sDirSep << "report_aapt2_" << getpid() << ".json";
FILE* f = android::base::utf8::fopen(s.str().c_str(), "a");
if (f == nullptr) {
return;
}
+ // Wrap the trace in a JSON array [] to make Chrome/Perfetto UI handle it.
+ char delimiter = '[';
for(const TracePoint& trace : traces) {
- fprintf(f, "{\"ts\" : \"%" PRIu64 "\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", "
- "\"name\" : \"%s\" },\n", trace.time, trace.type, 0, trace.tid, trace.tag.c_str());
+ fprintf(f,
+ "%c{\"ts\" : \"%" PRIu64
+ "\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", \"name\" : \"%s\" }\n",
+ delimiter, trace.time, trace.type, 0, trace.tid, trace.tag.c_str());
+ delimiter = ',';
+ }
+ if (!traces.empty()) {
+ fprintf(f, "]");
}
fclose(f);
traces.clear();
@@ -86,66 +92,82 @@
} // namespace tracebuffer
-void BeginTrace(const std::string& tag) {
- tracebuffer::Add(tag, tracebuffer::kBegin);
+void BeginTrace(std::string tag) {
+ if (!tracebuffer::enabled) return;
+ tracebuffer::Add(std::move(tag), tracebuffer::kBegin);
}
-void EndTrace() {
- tracebuffer::Add("", tracebuffer::kEnd);
+void EndTrace(std::string tag) {
+ if (!tracebuffer::enabled) return;
+ tracebuffer::Add(std::move(tag), tracebuffer::kEnd);
}
-Trace::Trace(const std::string& tag) {
- tracebuffer::Add(tag, tracebuffer::kBegin);
+bool Trace::enable(bool value) {
+ return tracebuffer::enabled = value;
}
-Trace::Trace(const std::string& tag, const std::vector<android::StringPiece>& args) {
- std::stringstream s;
+Trace::Trace(const char* tag) {
+ if (!tracebuffer::enabled) return;
+ tag_.assign(tag);
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
+}
+
+Trace::Trace(std::string tag) : tag_(std::move(tag)) {
+ if (!tracebuffer::enabled) return;
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
+}
+
+template <class SpanOfStrings>
+std::string makeTag(std::string_view tag, const SpanOfStrings& args) {
+ std::ostringstream s;
s << tag;
- s << " ";
- for (auto& arg : args) {
- s << arg;
- s << " ";
+ if (!args.empty()) {
+ for (const auto& arg : args) {
+ s << ' ';
+ s << arg;
+ }
}
- tracebuffer::Add(s.str(), tracebuffer::kBegin);
+ return std::move(s).str();
+}
+
+Trace::Trace(std::string_view tag, const std::vector<android::StringPiece>& args) {
+ if (!tracebuffer::enabled) return;
+ tag_ = makeTag(tag, args);
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
}
Trace::~Trace() {
- tracebuffer::Add("", tracebuffer::kEnd);
+ if (!tracebuffer::enabled) return;
+ tracebuffer::Add(std::move(tag_), tracebuffer::kEnd);
}
-FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag)
- : basepath_(basepath) {
- tracebuffer::Add(tag, tracebuffer::kBegin);
+FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag) {
+ if (!Trace::enable(!basepath.empty())) return;
+ basepath_.assign(basepath);
+ tag_.assign(tag);
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
}
-FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag,
- const std::vector<android::StringPiece>& args) : basepath_(basepath) {
- std::stringstream s;
- s << tag;
- s << " ";
- for (auto& arg : args) {
- s << arg;
- s << " ";
- }
- tracebuffer::Add(s.str(), tracebuffer::kBegin);
+FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag,
+ const std::vector<android::StringPiece>& args) {
+ if (!Trace::enable(!basepath.empty())) return;
+ basepath_.assign(basepath);
+ tag_ = makeTag(tag, args);
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
}
-FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag,
- const std::vector<std::string>& args) : basepath_(basepath){
- std::stringstream s;
- s << tag;
- s << " ";
- for (auto& arg : args) {
- s << arg;
- s << " ";
- }
- tracebuffer::Add(s.str(), tracebuffer::kBegin);
+FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag,
+ const std::vector<std::string>& args) {
+ if (!Trace::enable(!basepath.empty())) return;
+ basepath_.assign(basepath);
+ tag_ = makeTag(tag, args);
+ tracebuffer::Add(tag_, tracebuffer::kBegin);
}
FlushTrace::~FlushTrace() {
- tracebuffer::Add("", tracebuffer::kEnd);
+ if (!tracebuffer::enabled) return;
+ tracebuffer::Add(tag_, tracebuffer::kEnd);
tracebuffer::Flush(basepath_);
}
-} // namespace aapt
-
+} // namespace aapt
diff --git a/tools/aapt2/trace/TraceBuffer.h b/tools/aapt2/trace/TraceBuffer.h
index ba751dd..f0333d1 100644
--- a/tools/aapt2/trace/TraceBuffer.h
+++ b/tools/aapt2/trace/TraceBuffer.h
@@ -17,41 +17,50 @@
#ifndef AAPT_TRACEBUFFER_H
#define AAPT_TRACEBUFFER_H
-#include <string>
-#include <vector>
-
#include <androidfw/StringPiece.h>
+#include <string>
+#include <string_view>
+#include <vector>
+
namespace aapt {
// Record timestamps for beginning and end of a task and generate systrace json fragments.
// This is an in-process ftrace which has the advantage of being platform independent.
// These methods are NOT thread-safe since aapt2 is not multi-threaded.
-// Convenience RIAA object to automatically finish an event when object goes out of scope.
+// Convenience RAII object to automatically finish an event when object goes out of scope.
class Trace {
public:
- Trace(const std::string& tag);
- Trace(const std::string& tag, const std::vector<android::StringPiece>& args);
- ~Trace();
+ Trace(const char* tag);
+ Trace(std::string tag);
+ Trace(std::string_view tag, const std::vector<android::StringPiece>& args);
+ ~Trace();
+
+ static bool enable(bool value = true);
+
+private:
+ std::string tag_;
};
// Manual markers.
-void BeginTrace(const std::string& tag);
-void EndTrace();
+void BeginTrace(std::string tag);
+void EndTrace(std::string tag);
// A main trace is required to flush events to disk. Events are formatted in systrace
// json format.
class FlushTrace {
public:
- explicit FlushTrace(const std::string& basepath, const std::string& tag);
- explicit FlushTrace(const std::string& basepath, const std::string& tag,
- const std::vector<android::StringPiece>& args);
- explicit FlushTrace(const std::string& basepath, const std::string& tag,
- const std::vector<std::string>& args);
- ~FlushTrace();
+ explicit FlushTrace(std::string_view basepath, std::string_view tag);
+ explicit FlushTrace(std::string_view basepath, std::string_view tag,
+ const std::vector<android::StringPiece>& args);
+ explicit FlushTrace(std::string_view basepath, std::string_view tag,
+ const std::vector<std::string>& args);
+ ~FlushTrace();
+
private:
std::string basepath_;
+ std::string tag_;
};
#define TRACE_CALL() Trace __t(__func__)