Merge "Add TestAPI to replace content on a display." into main
diff --git a/Android.bp b/Android.bp
index 93d6f53..be589b2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -246,7 +246,6 @@
"android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
- "com.android.sysprop.apex",
"com.android.sysprop.init",
"com.android.sysprop.localization",
"PlatformProperties",
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index fb342b9..913a76a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -61,6 +61,7 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
@@ -802,6 +803,9 @@
}
}
}
+ } catch (FileNotFoundException e) {
+ // Expected on first boot
+ Slog.d(TAG, "App idle file for user " + userId + " does not exist");
} catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Unable to read app idle file for user " + userId, e);
} finally {
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 4089cfe..653e243 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -15,8 +15,6 @@
*/
package android.content.res;
-import static android.content.res.Resources.ID_NULL;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -388,7 +386,7 @@
synchronized (this) {
long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
- XmlResourceParser parser = block.newParser(ID_NULL, new Validator());
+ XmlResourceParser parser = block.newParser();
// If nativeOpenXml doesn't throw, it will always return a valid native pointer,
// which makes newParser always return non-null. But let's be careful.
if (parser == null) {
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index a6fea6a..62a46b6 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -17,7 +17,6 @@
package android.content.res;
import android.annotation.NonNull;
-import android.util.ArrayMap;
import android.util.Pools.SimplePool;
import androidx.annotation.StyleableRes;
@@ -27,9 +26,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.util.Iterator;
-import java.util.Map;
-
/**
* Defines the string attribute length and child tag count restrictions for a xml element.
*
@@ -161,9 +157,11 @@
private static final String[] NAME_VALUE_ATTRS = {TAG_ATTR_NAME, TAG_ATTR_VALUE};
private String[] mStringAttrNames = new String[0];
- private final Map<String, TagCounter> mTagCounters = new ArrayMap<>();
+ // The length of mTagCounters corresponds to the number of tags defined in getCounterIdx. If new
+ // tags are added then the size here should be increased to match.
+ private final TagCounter[] mTagCounters = new TagCounter[35];
- private String mTag;
+ String mTag;
private static final ThreadLocal<SimplePool<Element>> sPool =
ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE));
@@ -180,17 +178,91 @@
void recycle() {
mStringAttrNames = new String[0];
- Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator();
- while (it.hasNext()) {
- it.next().getValue().recycle();
- it.remove();
- }
mTag = null;
sPool.get().release(this);
}
+ private long mChildTagMask = 0;
+
+ private static int getCounterIdx(String tag) {
+ switch(tag) {
+ case TAG_LAYOUT:
+ return 0;
+ case TAG_META_DATA:
+ return 1;
+ case TAG_INTENT_FILTER:
+ return 2;
+ case TAG_PROFILEABLE:
+ return 3;
+ case TAG_USES_NATIVE_LIBRARY:
+ return 4;
+ case TAG_RECEIVER:
+ return 5;
+ case TAG_SERVICE:
+ return 6;
+ case TAG_ACTIVITY_ALIAS:
+ return 7;
+ case TAG_USES_LIBRARY:
+ return 8;
+ case TAG_PROVIDER:
+ return 9;
+ case TAG_ACTIVITY:
+ return 10;
+ case TAG_ACTION:
+ return 11;
+ case TAG_CATEGORY:
+ return 12;
+ case TAG_DATA:
+ return 13;
+ case TAG_APPLICATION:
+ return 14;
+ case TAG_OVERLAY:
+ return 15;
+ case TAG_INSTRUMENTATION:
+ return 16;
+ case TAG_PERMISSION_GROUP:
+ return 17;
+ case TAG_PERMISSION_TREE:
+ return 18;
+ case TAG_SUPPORTS_GL_TEXTURE:
+ return 19;
+ case TAG_SUPPORTS_SCREENS:
+ return 20;
+ case TAG_USES_CONFIGURATION:
+ return 21;
+ case TAG_USES_PERMISSION_SDK_23:
+ return 22;
+ case TAG_USES_SDK:
+ return 23;
+ case TAG_COMPATIBLE_SCREENS:
+ return 24;
+ case TAG_QUERIES:
+ return 25;
+ case TAG_ATTRIBUTION:
+ return 26;
+ case TAG_USES_FEATURE:
+ return 27;
+ case TAG_PERMISSION:
+ return 28;
+ case TAG_USES_PERMISSION:
+ return 29;
+ case TAG_GRANT_URI_PERMISSION:
+ return 30;
+ case TAG_PATH_PERMISSION:
+ return 31;
+ case TAG_PACKAGE:
+ return 32;
+ case TAG_INTENT:
+ return 33;
+ default:
+ // The size of the mTagCounters array should be equal to this value+1
+ return 34;
+ }
+ }
+
private void init(String tag) {
this.mTag = tag;
+ mChildTagMask = 0;
switch (tag) {
case TAG_ACTION:
case TAG_CATEGORY:
@@ -208,29 +280,29 @@
break;
case TAG_ACTIVITY:
setStringAttrNames(ACTIVITY_STR_ATTR_NAMES);
- addTagCounter(1000, TAG_LAYOUT);
- addTagCounter(8000, TAG_META_DATA);
- addTagCounter(20000, TAG_INTENT_FILTER);
+ initializeCounter(TAG_LAYOUT, 1000);
+ initializeCounter(TAG_META_DATA, 8000);
+ initializeCounter(TAG_INTENT_FILTER, 20000);
break;
case TAG_ACTIVITY_ALIAS:
setStringAttrNames(ACTIVITY_ALIAS_STR_ATTR_NAMES);
- addTagCounter(8000, TAG_META_DATA);
- addTagCounter(20000, TAG_INTENT_FILTER);
+ initializeCounter(TAG_META_DATA, 8000);
+ initializeCounter(TAG_INTENT_FILTER, 20000);
break;
case TAG_APPLICATION:
setStringAttrNames(APPLICATION_STR_ATTR_NAMES);
- addTagCounter(100, TAG_PROFILEABLE);
- addTagCounter(100, TAG_USES_NATIVE_LIBRARY);
- addTagCounter(1000, TAG_RECEIVER);
- addTagCounter(1000, TAG_SERVICE);
- addTagCounter(4000, TAG_ACTIVITY_ALIAS);
- addTagCounter(4000, TAG_USES_LIBRARY);
- addTagCounter(8000, TAG_PROVIDER);
- addTagCounter(8000, TAG_META_DATA);
- addTagCounter(40000, TAG_ACTIVITY);
+ initializeCounter(TAG_PROFILEABLE, 100);
+ initializeCounter(TAG_USES_NATIVE_LIBRARY, 100);
+ initializeCounter(TAG_RECEIVER, 1000);
+ initializeCounter(TAG_SERVICE, 1000);
+ initializeCounter(TAG_ACTIVITY_ALIAS, 4000);
+ initializeCounter(TAG_USES_LIBRARY, 4000);
+ initializeCounter(TAG_PROVIDER, 8000);
+ initializeCounter(TAG_META_DATA, 8000);
+ initializeCounter(TAG_ACTIVITY, 40000);
break;
case TAG_COMPATIBLE_SCREENS:
- addTagCounter(4000, TAG_SCREEN);
+ initializeCounter(TAG_SCREEN, 4000);
break;
case TAG_DATA:
setStringAttrNames(DATA_STR_ATTR_NAMES);
@@ -243,28 +315,28 @@
break;
case TAG_INTENT:
case TAG_INTENT_FILTER:
- addTagCounter(20000, TAG_ACTION);
- addTagCounter(40000, TAG_CATEGORY);
- addTagCounter(40000, TAG_DATA);
+ initializeCounter(TAG_ACTION, 20000);
+ initializeCounter(TAG_CATEGORY, 40000);
+ initializeCounter(TAG_DATA, 40000);
break;
case TAG_MANIFEST:
setStringAttrNames(MANIFEST_STR_ATTR_NAMES);
- addTagCounter(100, TAG_APPLICATION);
- addTagCounter(100, TAG_OVERLAY);
- addTagCounter(100, TAG_INSTRUMENTATION);
- addTagCounter(100, TAG_PERMISSION_GROUP);
- addTagCounter(100, TAG_PERMISSION_TREE);
- addTagCounter(100, TAG_SUPPORTS_GL_TEXTURE);
- addTagCounter(100, TAG_SUPPORTS_SCREENS);
- addTagCounter(100, TAG_USES_CONFIGURATION);
- addTagCounter(100, TAG_USES_PERMISSION_SDK_23);
- addTagCounter(100, TAG_USES_SDK);
- addTagCounter(200, TAG_COMPATIBLE_SCREENS);
- addTagCounter(200, TAG_QUERIES);
- addTagCounter(400, TAG_ATTRIBUTION);
- addTagCounter(400, TAG_USES_FEATURE);
- addTagCounter(2000, TAG_PERMISSION);
- addTagCounter(20000, TAG_USES_PERMISSION);
+ initializeCounter(TAG_APPLICATION, 100);
+ initializeCounter(TAG_OVERLAY, 100);
+ initializeCounter(TAG_INSTRUMENTATION, 100);
+ initializeCounter(TAG_PERMISSION_GROUP, 100);
+ initializeCounter(TAG_PERMISSION_TREE, 100);
+ initializeCounter(TAG_SUPPORTS_GL_TEXTURE, 100);
+ initializeCounter(TAG_SUPPORTS_SCREENS, 100);
+ initializeCounter(TAG_USES_CONFIGURATION, 100);
+ initializeCounter(TAG_USES_PERMISSION_SDK_23, 100);
+ initializeCounter(TAG_USES_SDK, 100);
+ initializeCounter(TAG_COMPATIBLE_SCREENS, 200);
+ initializeCounter(TAG_QUERIES, 200);
+ initializeCounter(TAG_ATTRIBUTION, 400);
+ initializeCounter(TAG_USES_FEATURE, 400);
+ initializeCounter(TAG_PERMISSION, 2000);
+ initializeCounter(TAG_USES_PERMISSION, 20000);
break;
case TAG_META_DATA:
case TAG_PROPERTY:
@@ -281,21 +353,21 @@
break;
case TAG_PROVIDER:
setStringAttrNames(PROVIDER_STR_ATTR_NAMES);
- addTagCounter(100, TAG_GRANT_URI_PERMISSION);
- addTagCounter(100, TAG_PATH_PERMISSION);
- addTagCounter(8000, TAG_META_DATA);
- addTagCounter(20000, TAG_INTENT_FILTER);
+ initializeCounter(TAG_GRANT_URI_PERMISSION, 100);
+ initializeCounter(TAG_PATH_PERMISSION, 100);
+ initializeCounter(TAG_META_DATA, 8000);
+ initializeCounter(TAG_INTENT_FILTER, 20000);
break;
case TAG_QUERIES:
- addTagCounter(1000, TAG_PACKAGE);
- addTagCounter(2000, TAG_INTENT);
- addTagCounter(8000, TAG_PROVIDER);
+ initializeCounter(TAG_PACKAGE, 1000);
+ initializeCounter(TAG_INTENT, 2000);
+ initializeCounter(TAG_PROVIDER, 8000);
break;
case TAG_RECEIVER:
case TAG_SERVICE:
setStringAttrNames(RECEIVER_SERVICE_STR_ATTR_NAMES);
- addTagCounter(8000, TAG_META_DATA);
- addTagCounter(20000, TAG_INTENT_FILTER);
+ initializeCounter(TAG_META_DATA, 8000);
+ initializeCounter(TAG_INTENT_FILTER, 20000);
break;
}
}
@@ -365,12 +437,17 @@
}
}
- private void addTagCounter(int max, String tag) {
- mTagCounters.put(tag, TagCounter.obtain(max));
+ private void initializeCounter(String tag, int max) {
+ int idx = getCounterIdx(tag);
+ if (mTagCounters[idx] == null) {
+ mTagCounters[idx] = new TagCounter();
+ }
+ mTagCounters[idx].reset(max);
+ mChildTagMask |= 1 << idx;
}
boolean hasChild(String tag) {
- return mTagCounters.containsKey(tag);
+ return (mChildTagMask & (1 << getCounterIdx(tag))) != 0;
}
void validateStringAttrs(@NonNull XmlPullParser attrs) throws XmlPullParserException {
@@ -393,8 +470,8 @@
}
void seen(@NonNull Element element) throws XmlPullParserException {
- if (mTagCounters.containsKey(element.mTag)) {
- TagCounter counter = mTagCounters.get(element.mTag);
+ TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
+ if (counter != null) {
counter.increment();
if (!counter.isValid()) {
throw new XmlPullParserException("The number of child " + element.mTag
diff --git a/core/java/android/content/res/TagCounter.java b/core/java/android/content/res/TagCounter.java
index 0e5510f..94deee7 100644
--- a/core/java/android/content/res/TagCounter.java
+++ b/core/java/android/content/res/TagCounter.java
@@ -16,47 +16,25 @@
package android.content.res;
-import android.annotation.NonNull;
-import android.util.Pools.SimplePool;
-
/**
* Counter used to track the number of tags seen during manifest validation.
*
* {@hide}
*/
public class TagCounter {
- private static final int MAX_POOL_SIZE = 512;
private static final int DEFAULT_MAX_COUNT = 512;
- private static final ThreadLocal<SimplePool<TagCounter>> sPool =
- ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE));
-
private int mMaxValue;
private int mCount;
- @NonNull
- static TagCounter obtain(int max) {
- TagCounter counter = sPool.get().acquire();
- if (counter == null) {
- counter = new TagCounter();
- }
- counter.setMaxValue(max);
- return counter;
- }
-
- void recycle() {
- mCount = 0;
- mMaxValue = DEFAULT_MAX_COUNT;
- sPool.get().release(this);
- }
-
public TagCounter() {
mMaxValue = DEFAULT_MAX_COUNT;
mCount = 0;
}
- private void setMaxValue(int maxValue) {
+ void reset(int maxValue) {
this.mMaxValue = maxValue;
+ this.mCount = 0;
}
void increment() {
@@ -66,8 +44,4 @@
public boolean isValid() {
return mCount <= mMaxValue;
}
-
- int value() {
- return mCount;
- }
}
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
index daa7dfe..8b5e6c6 100644
--- a/core/java/android/content/res/Validator.java
+++ b/core/java/android/content/res/Validator.java
@@ -24,9 +24,6 @@
import org.xmlpull.v1.XmlPullParserException;
import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
/**
* Validates manifest files by ensuring that tag counts and the length of string attributes are
@@ -36,30 +33,12 @@
*/
public class Validator {
- private static final int MAX_TAG_COUNT = 100000;
-
private final ArrayDeque<Element> mElements = new ArrayDeque<>();
- private final Map<String, TagCounter> mTagCounters = new HashMap<>();
private void cleanUp() {
while (!mElements.isEmpty()) {
mElements.pop().recycle();
}
- Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator();
- while (it.hasNext()) {
- it.next().getValue().recycle();
- it.remove();
- }
- }
-
- private void seen(String tag) throws XmlPullParserException {
- mTagCounters.putIfAbsent(tag, TagCounter.obtain(MAX_TAG_COUNT));
- TagCounter counter = mTagCounters.get(tag);
- counter.increment();
- if (!counter.isValid()) {
- throw new XmlPullParserException("The number of " + tag
- + " tags exceeded " + MAX_TAG_COUNT);
- }
}
/**
@@ -84,7 +63,6 @@
}
Element parent = mElements.peek();
if (parent == null || parent.hasChild(tag)) {
- seen(tag);
Element element = Element.obtain(tag);
element.validateStringAttrs(parser);
if (parent != null) {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index aa67693..0461b2e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -547,6 +547,30 @@
}
/**
+ * Resolve default values into integer amplitude numbers.
+ *
+ * @param defaultAmplitude the default amplitude to apply, must be between 0 and
+ * MAX_AMPLITUDE
+ * @return this if amplitude value is already set, or a copy of this effect with given default
+ * amplitude otherwise
+ *
+ * @hide
+ */
+ public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude);
+
+ /**
+ * Scale the vibration effect intensity with the given constraints.
+ *
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up
+ * @return this if there is no scaling to be done, or a copy of this effect with scaled
+ * vibration intensity otherwise
+ *
+ * @hide
+ */
+ public abstract <T extends VibrationEffect> T scale(float scaleFactor);
+
+ /**
* Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
* should only be applied once to an original effect - it shouldn't be applied to the
* result of this method.
@@ -822,6 +846,40 @@
/** @hide */
@NonNull
@Override
+ public Composed resolve(int defaultAmplitude) {
+ int segmentCount = mSegments.size();
+ ArrayList<VibrationEffectSegment> resolvedSegments = new ArrayList<>(segmentCount);
+ for (int i = 0; i < segmentCount; i++) {
+ resolvedSegments.add(mSegments.get(i).resolve(defaultAmplitude));
+ }
+ if (resolvedSegments.equals(mSegments)) {
+ return this;
+ }
+ Composed resolved = new Composed(resolvedSegments, mRepeatIndex);
+ resolved.validate();
+ return resolved;
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public Composed scale(float scaleFactor) {
+ int segmentCount = mSegments.size();
+ ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount);
+ for (int i = 0; i < segmentCount; i++) {
+ scaledSegments.add(mSegments.get(i).scale(scaleFactor));
+ }
+ if (scaledSegments.equals(mSegments)) {
+ return this;
+ }
+ Composed scaled = new Composed(scaledSegments, mRepeatIndex);
+ scaled.validate();
+ return scaled;
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public Composed applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) {
boolean isRepeating = mRepeatIndex >= 0;
if (isRepeating == wantRepeating) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9f1cc71..a626775 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14830,7 +14830,8 @@
* view satisfies any of the following:
* <ul>
* <li>Is actionable, e.g. {@link #isClickable()},
- * {@link #isLongClickable()}, or {@link #isFocusable()}
+ * {@link #isLongClickable()}, {@link #isContextClickable()},
+ * {@link #isScreenReaderFocusable()}, or {@link #isFocusable()}
* <li>Has an {@link AccessibilityDelegate}
* <li>Has an interaction listener, e.g. {@link OnTouchListener},
* {@link OnKeyListener}, etc.
@@ -14839,6 +14840,7 @@
* {@link #ACCESSIBILITY_LIVE_REGION_NONE}.
* </ul>
* <li>Has an accessibility pane title, see {@link #setAccessibilityPaneTitle}</li>
+ * <li>Is an accessibility heading, see {@link #setAccessibilityHeading(boolean)}.</li>
* </ol>
*
* @return Whether the view is exposed for accessibility.
@@ -14865,7 +14867,7 @@
return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility()
|| hasListenersForAccessibility() || mAccessibilityDelegate != null
|| getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
- || isAccessibilityPane();
+ || isAccessibilityPane() || isAccessibilityHeading();
}
/**
@@ -15025,7 +15027,8 @@
* @hide
*/
public boolean isActionableForAccessibility() {
- return (isClickable() || isLongClickable() || isFocusable());
+ return (isClickable() || isLongClickable() || isFocusable() || isContextClickable()
+ || isScreenReaderFocusable());
}
/**
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 9107236..73954da 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -38,6 +38,8 @@
import android.hardware.vibrator.IVibrator;
import android.net.Uri;
import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.StepSegment;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -634,6 +636,88 @@
.validate());
}
+ @Test
+ public void testResolveOneShot() {
+ VibrationEffect.Composed resolved = DEFAULT_ONE_SHOT.resolve(51);
+ assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude());
+
+ assertThrows(IllegalArgumentException.class, () -> DEFAULT_ONE_SHOT.resolve(1000));
+ }
+
+ @Test
+ public void testResolveWaveform() {
+ VibrationEffect.Composed resolved = TEST_WAVEFORM.resolve(102);
+ assertEquals(0.4f, ((StepSegment) resolved.getSegments().get(2)).getAmplitude());
+
+ assertThrows(IllegalArgumentException.class, () -> TEST_WAVEFORM.resolve(1000));
+ }
+
+ @Test
+ public void testResolvePrebaked() {
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ assertEquals(effect, effect.resolve(51));
+ }
+
+ @Test
+ public void testResolveComposed() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 1)
+ .compose();
+ assertEquals(effect, effect.resolve(51));
+
+ VibrationEffect.Composed resolved = VibrationEffect.startComposition()
+ .addEffect(DEFAULT_ONE_SHOT)
+ .compose()
+ .resolve(51);
+ assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude());
+ }
+
+ @Test
+ public void testScaleOneShot() {
+ VibrationEffect.Composed scaledUp = TEST_ONE_SHOT.scale(1.5f);
+ assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude());
+
+ VibrationEffect.Composed scaledDown = TEST_ONE_SHOT.scale(0.5f);
+ assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude());
+ }
+
+ @Test
+ public void testScaleWaveform() {
+ VibrationEffect.Composed scaledUp = TEST_WAVEFORM.scale(1.5f);
+ assertEquals(1f, ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude(), 1e-5f);
+
+ VibrationEffect.Composed scaledDown = TEST_WAVEFORM.scale(0.5f);
+ assertTrue(1f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude());
+ }
+
+ @Test
+ public void testScalePrebaked() {
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+ VibrationEffect.Composed scaledUp = effect.scale(1.5f);
+ assertEquals(effect, scaledUp);
+
+ VibrationEffect.Composed scaledDown = effect.scale(0.5f);
+ assertEquals(effect, scaledDown);
+ }
+
+ @Test
+ public void testScaleComposed() {
+ VibrationEffect.Composed effect =
+ (VibrationEffect.Composed) VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
+ .addEffect(TEST_ONE_SHOT)
+ .compose();
+
+ VibrationEffect.Composed scaledUp = effect.scale(1.5f);
+ assertTrue(0.5f < ((PrimitiveSegment) scaledUp.getSegments().get(0)).getScale());
+ assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(1)).getAmplitude());
+
+ VibrationEffect.Composed scaledDown = effect.scale(0.5f);
+ assertTrue(0.5f > ((PrimitiveSegment) scaledDown.getSegments().get(0)).getScale());
+ assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude());
+ }
+
private void doTestApplyRepeatingWithNonRepeatingOriginal(@NotNull VibrationEffect original) {
assertTrue(original.getDuration() != Long.MAX_VALUE);
int loopDelayMs = 123;
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index d7ac501..dbd9ef3 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -79,7 +79,7 @@
}
// flush will create a GrRenderTarget if not already present.
- canvas->flush();
+ directContext->flushAndSubmit();
GLuint fboID = 0;
SkISize fboSize;
@@ -167,7 +167,7 @@
// GL ops get inserted here if previous flush is missing, which could dirty the stencil
bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
- tmpCanvas->flush(); // need this flush for the single op that draws into the stencil
+ directContext->flushAndSubmit(); // need this flush for the single op that draws into the stencil
// ensure that the framebuffer that the webview will render into is bound before after we
// draw into the stencil
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 447cd7b..fe51ed5 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -321,4 +321,7 @@
<!-- Whether vibrate icon is shown in the status bar by default. -->
<integer name="def_statusBarVibrateIconEnabled">0</integer>
+
+ <!-- Whether predictive back animation is enabled by default. -->
+ <bool name="def_enable_back_animation">false</bool>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7a60507..d1d745f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -246,6 +246,7 @@
public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
public static final String RESULT_SETTINGS_LIST = "result_settings_list";
+ public static final String SETTINGS_PROVIDER_JOBS_NS = "SettingsProviderJobsNamespace";
// Used for scheduling jobs to make a copy for the settings files
public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1;
public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L;
@@ -2785,12 +2786,13 @@
*/
public void scheduleWriteFallbackFilesJob() {
final Context context = getContext();
- final JobScheduler jobScheduler =
+ JobScheduler jobScheduler =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (jobScheduler == null) {
// Might happen: SettingsProvider is created before JobSchedulerService in system server
return;
}
+ jobScheduler = jobScheduler.forNamespace(SETTINGS_PROVIDER_JOBS_NS);
// Check if the job is already scheduled. If so, skip scheduling another one
if (jobScheduler.getPendingJob(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) != null) {
return;
@@ -3773,7 +3775,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 220;
+ private static final int SETTINGS_VERSION = 221;
private final int mUserId;
@@ -5867,6 +5869,21 @@
currentVersion = 220;
}
+ if (currentVersion == 220) {
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting enableBackAnimation =
+ globalSettings.getSettingLocked(Global.ENABLE_BACK_ANIMATION);
+ if (enableBackAnimation.isNull()) {
+ final boolean defEnableBackAnimation =
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_enable_back_animation);
+ initGlobalSettingsDefaultValLocked(
+ Settings.Global.ENABLE_BACK_ANIMATION, defEnableBackAnimation);
+ }
+ currentVersion = 221;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
index 66aa7ba..91e8bf8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_PROVIDER_JOBS_NS;
import static com.android.providers.settings.SettingsProvider.TABLE_CONFIG;
import static com.android.providers.settings.SettingsProvider.TABLE_GLOBAL;
import static com.android.providers.settings.SettingsProvider.TABLE_SECURE;
@@ -35,7 +36,8 @@
public class WriteFallbackSettingsFilesJobService extends JobService {
@Override
public boolean onStartJob(final JobParameters params) {
- if (params.getJobId() != WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) {
+ if (!SETTINGS_PROVIDER_JOBS_NS.equals(params.getJobNamespace())
+ || params.getJobId() != WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) {
return false;
}
final List<String> settingsFiles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index f68bd49..35cf4a1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
import com.android.systemui.volume.dagger.VolumeModule;
+import com.android.systemui.wallpapers.dagger.WallpaperModule;
import dagger.Binds;
import dagger.Module;
@@ -106,6 +107,7 @@
StatusBarEventsModule.class,
StartCentralSurfacesModule.class,
VolumeModule.class,
+ WallpaperModule.class,
KeyboardShortcutsModule.class
})
public abstract class ReferenceSystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index b1f513d..a560acc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.statusbar.phone.LockscreenWallpaper
+import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
@@ -59,6 +60,7 @@
import com.android.systemui.util.NotificationChannels
import com.android.systemui.util.StartBinderLoggerModule
import com.android.systemui.volume.VolumeUI
+import com.android.systemui.wallpapers.dagger.WallpaperModule
import com.android.systemui.wmshell.WMShell
import dagger.Binds
import dagger.Module
@@ -72,6 +74,7 @@
MultiUserUtilsModule::class,
StartControlsStartableModule::class,
StartBinderLoggerModule::class,
+ WallpaperModule::class,
])
abstract class SystemUICoreStartableModule {
/** Inject into AuthController. */
@@ -316,4 +319,9 @@
@IntoMap
@ClassKey(LockscreenWallpaper::class)
abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(ScrimController::class)
+ abstract fun bindScrimController(impl: ScrimController): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 278ae95..72915a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3565,6 +3565,10 @@
}
};
+ /**
+ * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository}
+ * instead.
+ */
private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3584,7 +3588,6 @@
&& (info != null && info.supportsAmbientMode());
mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e4e912e..33bf06a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -48,6 +48,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
+import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -71,8 +72,10 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.AlarmTimeout;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -89,7 +92,8 @@
* security method gets shown).
*/
@SysUISingleton
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable,
+ CoreStartable {
static final String TAG = "ScrimController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -207,6 +211,7 @@
private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
private final Handler mHandler;
private final Executor mMainExecutor;
+ private final JavaAdapter mJavaAdapter;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -268,6 +273,7 @@
private boolean mKeyguardOccluded;
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final WallpaperRepository mWallpaperRepository;
private CoroutineDispatcher mMainDispatcher;
private boolean mIsBouncerToGoneTransitionRunning = false;
private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@@ -297,11 +303,13 @@
DockManager dockManager,
ConfigurationController configurationController,
@Main Executor mainExecutor,
+ JavaAdapter javaAdapter,
ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
KeyguardTransitionInteractor keyguardTransitionInteractor,
+ WallpaperRepository wallpaperRepository,
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
FeatureFlags featureFlags) {
@@ -317,6 +325,7 @@
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
mMainExecutor = mainExecutor;
+ mJavaAdapter = javaAdapter;
mScreenOffAnimationController = screenOffAnimationController;
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", mHandler);
@@ -348,9 +357,17 @@
mColors = new GradientColors();
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mWallpaperRepository = wallpaperRepository;
mMainDispatcher = mainDispatcher;
}
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mWallpaperRepository.getWallpaperSupportsAmbientMode(),
+ this::setWallpaperSupportsAmbientMode);
+ }
+
/**
* Attach the controller to the supplied views.
*/
@@ -1551,7 +1568,7 @@
pw.println(mState.getMaxLightRevealScrimAlpha());
}
- public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
+ private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index 5e489b0..82589d3 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.wallpapers.dagger.NoopWallpaperModule;
import dagger.Subcomponent;
@@ -39,6 +40,7 @@
DefaultComponentBinder.class,
DependencyProvider.class,
KeyguardModule.class,
+ NoopWallpaperModule.class,
NotificationRowModule.class,
NotificationsModule.class,
RecentsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt
new file mode 100644
index 0000000..baf88b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.wallpapers.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.wallpapers.data.repository.NoopWallpaperRepository
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface NoopWallpaperModule {
+ @Binds
+ @SysUISingleton
+ fun bindWallpaperRepository(impl: NoopWallpaperRepository): WallpaperRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt
new file mode 100644
index 0000000..1b89978
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.wallpapers.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
+import com.android.systemui.wallpapers.data.repository.WallpaperRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface WallpaperModule {
+ @Binds
+ @SysUISingleton
+ fun bindWallpaperRepository(impl: WallpaperRepositoryImpl): WallpaperRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
new file mode 100644
index 0000000..a640589
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.wallpapers.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A no-op implementation of [WallpaperRepository].
+ *
+ * Used for variants of SysUI that do not support wallpaper but require other SysUI classes that
+ * have a wallpaper dependency.
+ */
+@SysUISingleton
+class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
+ override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
new file mode 100644
index 0000000..48895ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.wallpapers.data.repository
+
+import android.app.WallpaperManager
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+/** A repository storing information about the current wallpaper. */
+interface WallpaperRepository {
+ /** Emits true if the current user's current wallpaper supports ambient mode. */
+ val wallpaperSupportsAmbientMode: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class WallpaperRepositoryImpl
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ broadcastDispatcher: BroadcastDispatcher,
+ userRepository: UserRepository,
+ private val wallpaperManager: WallpaperManager,
+ context: Context,
+) : WallpaperRepository {
+ private val deviceSupportsAodWallpaper =
+ context.resources.getBoolean(com.android.internal.R.bool.config_dozeSupportsAodWallpaper)
+
+ private val wallpaperChanged: Flow<Unit> =
+ broadcastDispatcher
+ .broadcastFlow(
+ IntentFilter(Intent.ACTION_WALLPAPER_CHANGED),
+ user = UserHandle.ALL,
+ )
+ // The `combine` defining `wallpaperSupportsAmbientMode` will not run until both of the
+ // input flows emit at least once. Since this flow is an input flow, it needs to emit
+ // when it starts up to ensure that the `combine` will run if the user changes before we
+ // receive a ACTION_WALLPAPER_CHANGED intent.
+ // Note that the `selectedUser` flow does *not* need to emit on start because
+ // [UserRepository.selectedUser] is a state flow which will automatically emit a value
+ // on start.
+ .onStart { emit(Unit) }
+
+ private val selectedUser: Flow<SelectedUserModel> =
+ userRepository.selectedUser
+ // Only update the wallpaper status once the user selection has finished.
+ .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
+
+ override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) {
+ MutableStateFlow(false).asStateFlow()
+ } else {
+ combine(wallpaperChanged, selectedUser) { _, selectedUser ->
+ doesWallpaperSupportAmbientMode(selectedUser)
+ }
+ .stateIn(
+ scope,
+ // Always be listening for wallpaper changes.
+ SharingStarted.Eagerly,
+ initialValue =
+ doesWallpaperSupportAmbientMode(userRepository.selectedUser.value),
+ )
+ }
+
+ private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean {
+ return wallpaperManager
+ .getWallpaperInfoForUser(
+ selectedUser.userInfo.id,
+ )
+ // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
+ ?.supportsAmbientMode() == true
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index bbc75c9..0244e7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -59,6 +59,7 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
@@ -77,9 +78,11 @@
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.utils.os.FakeHandler;
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
import com.google.common.truth.Expect;
@@ -100,6 +103,7 @@
import java.util.Map;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -113,6 +117,9 @@
private final LargeScreenShadeInterpolator
mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator();
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+
private ScrimController mScrimController;
private ScrimView mScrimBehind;
private ScrimView mNotificationsScrim;
@@ -136,6 +143,7 @@
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
@Mock private CoroutineDispatcher mMainDispatcher;
@Mock private TypedArray mMockTypedArray;
@@ -274,20 +282,26 @@
mDockManager,
mConfigurationController,
new FakeExecutor(new FakeSystemClock()),
+ mJavaAdapter,
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
+ mWallpaperRepository,
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);
+ mScrimController.start();
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
mScrimController.setHasBackdrop(false);
- mScrimController.setWallpaperSupportsAmbientMode(false);
+
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.KEYGUARD);
finishAnimationsImmediately();
}
@@ -388,7 +402,9 @@
@Test
public void transitionToAod_withAodWallpaper() {
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
@@ -410,7 +426,9 @@
@Test
public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
mScrimController.setHasBackdrop(true);
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
@@ -427,7 +445,9 @@
@Test
public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
mScrimController.setHasBackdrop(true);
@@ -540,7 +560,9 @@
// Pre-condition
// Need to go to AoD first because PULSING doesn't change
// the back scrim opacity - otherwise it would hide AoD wallpapers.
- mScrimController.setWallpaperSupportsAmbientMode(false);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
@@ -968,19 +990,23 @@
mDockManager,
mConfigurationController,
new FakeExecutor(new FakeSystemClock()),
+ mJavaAdapter,
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
+ mWallpaperRepository,
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);
+ mScrimController.start();
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
mScrimController.setHasBackdrop(false);
- mScrimController.setWallpaperSupportsAmbientMode(false);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
+ mTestScope.getTestScheduler().runCurrent();
mScrimController.transitionTo(ScrimState.KEYGUARD);
finishAnimationsImmediately();
@@ -1105,7 +1131,9 @@
@Test
public void testWillHideAodWallpaper() {
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1116,7 +1144,8 @@
public void testWillHideDockedWallpaper() {
mAlwaysOnEnabled = false;
when(mDockManager.isDocked()).thenReturn(true);
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
mScrimController.transitionTo(ScrimState.AOD);
@@ -1165,7 +1194,9 @@
@Test
public void testHidesShowWhenLockedActivity() {
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.setKeyguardOccluded(true);
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
@@ -1182,7 +1213,9 @@
@Test
public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
- mScrimController.setWallpaperSupportsAmbientMode(true);
+ mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
+ mTestScope.getTestScheduler().runCurrent();
+
mScrimController.transitionTo(ScrimState.AOD);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
new file mode 100644
index 0000000..6fc36b0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpapers.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of the wallpaper repository. */
+class FakeWallpaperRepository : WallpaperRepository {
+ override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
new file mode 100644
index 0000000..132b9b4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -0,0 +1,262 @@
+/*
+ * 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.wallpapers.data.repository
+
+import android.app.WallpaperInfo
+import android.app.WallpaperManager
+import android.content.Intent
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class WallpaperRepositoryImplTest : SysuiTestCase() {
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val userRepository = FakeUserRepository()
+ private val wallpaperManager: WallpaperManager = mock()
+
+ private val underTest: WallpaperRepositoryImpl by lazy {
+ WallpaperRepositoryImpl(
+ testScope.backgroundScope,
+ fakeBroadcastDispatcher,
+ userRepository,
+ wallpaperManager,
+ context,
+ )
+ }
+
+ @Before
+ fun setUp() {
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+ true,
+ )
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_nullInfo_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_infoDoesNotSupport_false() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_infoSupports_true() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_initialValueIsFetched_true() =
+ testScope.runTest {
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+ .thenReturn(SUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP))
+ userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+ // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
+ // value for the wallpaper
+ assertThat(underTest.wallpaperSupportsAmbientMode.value).isTrue()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_initialValueIsFetched_false() =
+ testScope.runTest {
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
+ .thenReturn(UNSUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_UNSUPPORTED_WP))
+ userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP)
+
+ // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
+ // value for the wallpaper
+ assertThat(underTest.wallpaperSupportsAmbientMode.value).isFalse()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_updatesOnUserChanged() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+ .thenReturn(SUPPORTED_WP)
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
+ .thenReturn(UNSUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP))
+
+ // WHEN a user with supported wallpaper is selected
+ userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+ // THEN it's true
+ assertThat(latest).isTrue()
+
+ // WHEN the user is switched to a user with unsupported wallpaper
+ userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP)
+
+ // THEN it's false
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_doesNotUpdateOnUserChanging() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+ .thenReturn(SUPPORTED_WP)
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
+ .thenReturn(UNSUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP))
+
+ // WHEN a user with supported wallpaper is selected
+ userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+ // THEN it's true
+ assertThat(latest).isTrue()
+
+ // WHEN the user has started switching to a user with unsupported wallpaper but hasn't
+ // finished yet
+ userRepository.selectedUser.value =
+ SelectedUserModel(USER_WITH_UNSUPPORTED_WP, SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // THEN it still matches the old user
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_updatesOnIntent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+
+ val info: WallpaperInfo = mock()
+ whenever(info.supportsAmbientMode()).thenReturn(false)
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info)
+
+ assertThat(latest).isFalse()
+
+ // WHEN the info now supports ambient mode and a broadcast is sent
+ whenever(info.supportsAmbientMode()).thenReturn(true)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the flow updates
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_wallpaperNotSupported_alwaysFalse() =
+ testScope.runTest {
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(false)
+
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+ assertThat(latest).isFalse()
+
+ // Even WHEN the current wallpaper *does* support ambient mode
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still false because wallpaper isn't supported
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun wallpaperSupportsAmbientMode_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+ testScope.runTest {
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+ false
+ )
+
+ val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+ assertThat(latest).isFalse()
+
+ // Even WHEN the current wallpaper *does* support ambient mode
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still false because the device doesn't support it
+ assertThat(latest).isFalse()
+ }
+
+ private companion object {
+ val USER_WITH_UNSUPPORTED_WP = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+ val UNSUPPORTED_WP =
+ mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(false) }
+
+ val USER_WITH_SUPPORTED_WP = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+ val SUPPORTED_WP =
+ mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(true) }
+ }
+}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index bca2d60..caf1684 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -35,7 +35,6 @@
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
-import android.sysprop.ApexProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1208,8 +1207,7 @@
boolean systemExt = permFile.toPath().startsWith(
Environment.getSystemExtDirectory().toPath() + "/");
boolean apex = permFile.toPath().startsWith(
- Environment.getApexDirectory().toPath() + "/")
- && ApexProperties.updatable().orElse(false);
+ Environment.getApexDirectory().toPath() + "/");
if (vendor) {
readPrivAppPermissions(parser,
mPermissionAllowlist.getVendorPrivilegedAppAllowlist());
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index f4c8a57..146215b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -357,15 +357,25 @@
@Override
public void onPackageAdded(String packageName, int uid) {
// Called on a handler, and running as the system
- UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
- cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts);
+ try {
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
+ cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.w(TAG, "Can't read accounts database", e);
+ return;
+ }
}
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
// Called on a handler, and running as the system
- UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
- cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts);
+ try {
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
+ cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.w(TAG, "Can't read accounts database", e);
+ return;
+ }
}
}.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
@@ -391,6 +401,9 @@
}
} catch (NameNotFoundException e) {
/* ignore */
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.w(TAG, "Can't read accounts database", e);
+ return;
}
}
});
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1dff62e..b6e58ad 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2124,11 +2124,7 @@
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- if (Process.isIsolated(uid)) {
- Slog.e(TAG, "Cannot setMode: isolated process");
- } else {
- Slog.e(TAG, "Cannot setMode", e);
- }
+ logVerifyAndGetBypassFailure(uid, e, "setMode");
return;
}
@@ -2580,11 +2576,7 @@
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- if (Process.isIsolated(uid)) {
- Slog.e(TAG, "Cannot checkOperation: isolated process");
- } else {
- Slog.e(TAG, "Cannot checkOperation", e);
- }
+ logVerifyAndGetBypassFailure(uid, e, "checkOperation");
return AppOpsManager.opToDefaultMode(code);
}
@@ -2796,11 +2788,7 @@
attributionTag = null;
}
} catch (SecurityException e) {
- if (Process.isIsolated(uid)) {
- Slog.e(TAG, "Cannot noteOperation: isolated process");
- } else {
- Slog.e(TAG, "Cannot noteOperation", e);
- }
+ logVerifyAndGetBypassFailure(uid, e, "noteOperation");
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3333,11 +3321,7 @@
attributionTag = null;
}
} catch (SecurityException e) {
- if (Process.isIsolated(uid)) {
- Slog.e(TAG, "Cannot startOperation: isolated process");
- } else {
- Slog.e(TAG, "Cannot startOperation", e);
- }
+ logVerifyAndGetBypassFailure(uid, e, "startOperation");
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3513,11 +3497,7 @@
attributionTag = null;
}
} catch (SecurityException e) {
- if (Process.isIsolated(uid)) {
- Slog.e(TAG, "Cannot finishOperation: isolated process");
- } else {
- Slog.e(TAG, "Cannot finishOperation", e);
- }
+ logVerifyAndGetBypassFailure(uid, e, "finishOperation");
return;
}
@@ -4073,6 +4053,17 @@
return false;
}
+ private void logVerifyAndGetBypassFailure(int uid, @NonNull SecurityException e,
+ @NonNull String methodName) {
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot " + methodName + ": isolated UID");
+ } else if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+ Slog.e(TAG, "Cannot " + methodName + ": non-application UID " + uid);
+ } else {
+ Slog.e(TAG, "Cannot " + methodName, e);
+ }
+ }
+
/**
* Get (and potentially create) ops.
*
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 9069eb2..38c6a52 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -193,8 +193,11 @@
if (IS_EMULATOR) {
mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
synchronized (mLock) {
- setPrimaryClipInternalLocked(getClipboardLocked(0, DEVICE_ID_DEFAULT), clip,
- android.os.Process.SYSTEM_UID, null);
+ Clipboard clipboard = getClipboardLocked(0, DEVICE_ID_DEFAULT);
+ if (clipboard != null) {
+ setPrimaryClipInternalLocked(clipboard, clip, android.os.Process.SYSTEM_UID,
+ null);
+ }
}
});
} else {
@@ -637,6 +640,9 @@
}
Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
+ if (clipboard == null) {
+ return null;
+ }
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
if (clipboard.primaryClip != null) {
@@ -665,7 +671,7 @@
}
synchronized (mLock) {
Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
- return clipboard.primaryClip != null
+ return (clipboard != null && clipboard.primaryClip != null)
? clipboard.primaryClip.getDescription() : null;
}
}
@@ -688,7 +694,8 @@
return false;
}
synchronized (mLock) {
- return getClipboardLocked(intendingUserId, intendingDeviceId).primaryClip != null;
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
+ return clipboard != null && clipboard.primaryClip != null;
}
}
@@ -709,8 +716,11 @@
return;
}
synchronized (mLock) {
- getClipboardLocked(intendingUserId, intendingDeviceId)
- .primaryClipListeners
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
+ if (clipboard == null) {
+ return;
+ }
+ clipboard.primaryClipListeners
.register(
listener,
new ListenerInfo(intendingUid, callingPackage, attributionTag));
@@ -733,8 +743,11 @@
return;
}
synchronized (mLock) {
- getClipboardLocked(intendingUserId,
- intendingDeviceId).primaryClipListeners.unregister(listener);
+ Clipboard clipboard = getClipboardLocked(intendingUserId,
+ intendingDeviceId);
+ if (clipboard != null) {
+ clipboard.primaryClipListeners.unregister(listener);
+ }
}
}
@@ -757,7 +770,7 @@
}
synchronized (mLock) {
Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
- if (clipboard.primaryClip != null) {
+ if (clipboard != null && clipboard.primaryClip != null) {
CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
return text != null && text.length() > 0;
}
@@ -786,7 +799,7 @@
}
synchronized (mLock) {
Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
- if (clipboard.primaryClip != null) {
+ if (clipboard != null && clipboard.primaryClip != null) {
return clipboard.mPrimaryClipPackage;
}
return null;
@@ -808,7 +821,8 @@
final int intendingUid = msg.arg2;
final int intendingDeviceId = ((Pair<Integer, Integer>) msg.obj).second;
synchronized (mLock) {
- if (getClipboardLocked(userId, intendingDeviceId).primaryClip != null) {
+ Clipboard clipboard = getClipboardLocked(userId, intendingDeviceId);
+ if (clipboard != null && clipboard.primaryClip != null) {
FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
setPrimaryClipInternalLocked(
@@ -824,9 +838,23 @@
};
@GuardedBy("mLock")
- private Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) {
+ private @Nullable Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) {
Clipboard clipboard = mClipboards.get(userId, deviceId);
if (clipboard == null) {
+ try {
+ if (!mUm.isUserRunning(userId)) {
+ Slog.w(TAG, "getClipboardLocked called with not running userId " + userId);
+ return null;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException calling UserManager: " + e);
+ return null;
+ }
+ if (deviceId != DEVICE_ID_DEFAULT && !mVdm.isValidVirtualDeviceId(deviceId)) {
+ Slog.w(TAG, "getClipboardLocked called with invalid (possibly released) deviceId "
+ + deviceId);
+ return null;
+ }
clipboard = new Clipboard(userId, deviceId);
mClipboards.add(userId, deviceId, clipboard);
}
@@ -876,8 +904,11 @@
final int userId = UserHandle.getUserId(uid);
// Update this user
- setPrimaryClipInternalLocked(getClipboardLocked(userId, deviceId), clip, uid,
- sourcePackage);
+ Clipboard clipboard = getClipboardLocked(userId, deviceId);
+ if (clipboard == null) {
+ return;
+ }
+ setPrimaryClipInternalLocked(clipboard, clip, uid, sourcePackage);
// Update related users
List<UserInfo> related = getRelatedProfiles(userId);
@@ -911,8 +942,11 @@
final boolean canCopyIntoProfile = !hasRestriction(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
- setPrimaryClipInternalNoClassifyLocked(
- getClipboardLocked(id, deviceId), clip, uid, sourcePackage);
+ Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
+ if (relatedClipboard != null) {
+ setPrimaryClipInternalNoClassifyLocked(relatedClipboard, clip, uid,
+ sourcePackage);
+ }
}
}
}
@@ -1046,6 +1080,9 @@
synchronized (mLock) {
Clipboard clipboard = getClipboardLocked(userId, deviceId);
+ if (clipboard == null) {
+ return;
+ }
if (clipboard.primaryClip == clip) {
applyClassificationAndSendBroadcastLocked(
clipboard, confidences, links, classifier);
@@ -1061,7 +1098,8 @@
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
- if (hasTextLocked(relatedClipboard, text)) {
+ if (relatedClipboard != null
+ && hasTextLocked(relatedClipboard, text)) {
applyClassificationAndSendBroadcastLocked(
relatedClipboard, confidences, links, classifier);
}
@@ -1184,9 +1222,10 @@
Binder.restoreCallingIdentity(oldIdentity);
}
Clipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid), deviceId);
- if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
+ if (clipboard != null && clipboard.primaryClip != null
+ && !clipboard.activePermissionOwners.contains(pkg)) {
final int N = clipboard.primaryClip.getItemCount();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid,
pkg, UserHandle.getUserId(uid));
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5e62b56..2206eac 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -35,7 +35,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
-import android.sysprop.ApexProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -82,18 +81,12 @@
new Singleton<ApexManager>() {
@Override
protected ApexManager create() {
- if (ApexProperties.updatable().orElse(false)) {
- return new ApexManagerImpl();
- } else {
- return new ApexManagerFlattenedApex();
- }
+ return new ApexManagerImpl();
}
};
/**
- * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
- * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
- * evaluates to {@code true}.
+ * Returns an instance of {@link ApexManagerImpl}
* @hide
*/
public static ApexManager getInstance() {
@@ -991,203 +984,4 @@
}
}
}
-
- /**
- * An implementation of {@link ApexManager} that should be used in case device does not support
- * updating APEX packages.
- */
- @VisibleForTesting
- static final class ApexManagerFlattenedApex extends ApexManager {
- @Override
- ApexInfo[] getAllApexInfos() {
- return null;
- }
-
- @Override
- void notifyScanResult(List<ScanResult> scanResults) {
- // No-op
- }
-
- @Override
- public List<ActiveApexInfo> getActiveApexInfos() {
- // There is no apexd running in case of flattened apex
- // We look up the /apex directory and identify the active APEX modules from there.
- // As "preinstalled" path, we just report /system since in the case of flattened APEX
- // the /apex directory is just a symlink to /system/apex.
- List<ActiveApexInfo> result = new ArrayList<>();
- File apexDir = Environment.getApexDirectory();
- if (apexDir.isDirectory()) {
- File[] files = apexDir.listFiles();
- // listFiles might be null if system server doesn't have permission to read
- // a directory.
- if (files != null) {
- for (File file : files) {
- if (file.isDirectory() && !file.getName().contains("@")
- // In flattened configuration, init special-cases the art directory
- // and bind-mounts com.android.art.debug to com.android.art.
- && !file.getName().equals("com.android.art.debug")) {
- result.add(
- new ActiveApexInfo(file, Environment.getRootDirectory(), file));
- }
- }
- }
- }
- return result;
- }
-
- @Override
- @Nullable
- public String getActiveApexPackageNameContainingPackage(
- @NonNull String containedPackageName) {
- Objects.requireNonNull(containedPackageName);
-
- return null;
- }
-
- @Override
- ApexSessionInfo getStagedSessionInfo(int sessionId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- SparseArray<ApexSessionInfo> getSessions() {
- return new SparseArray<>(0);
- }
-
- @Override
- ApexInfoList submitStagedSession(ApexSessionParams params)
- throws PackageManagerException {
- throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
- "Device doesn't support updating APEX");
- }
-
- @Override
- ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- void markStagedSessionReady(int sessionId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- void markStagedSessionSuccessful(int sessionId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- boolean isApexSupported() {
- return false;
- }
-
- @Override
- boolean revertActiveSessions() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- boolean abortStagedSession(int sessionId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- boolean uninstallApex(String apexPackagePath) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- void registerApkInApex(AndroidPackage pkg) {
- // No-op
- }
-
- @Override
- void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
- // No-op
- }
-
- @Override
- @Nullable
- String getApkInApexInstallError(String apexPackageName) {
- return null;
- }
-
- @Override
- List<String> getApksInApex(String apexPackageName) {
- return Collections.emptyList();
- }
-
- @Override
- @Nullable
- public String getApexModuleNameForPackageName(String apexPackageName) {
- return null;
- }
-
- @Override
- @Nullable
- public String getActivePackageNameForApexModuleName(String apexModuleName) {
- return null;
- }
-
- @Override
- public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean destroyDeSnapshots(int rollbackId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean destroyCeSnapshots(int userId, int rollbackId) {
- return true;
- }
-
- @Override
- public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
- return true;
- }
-
- @Override
- public void markBootCompleted() {
- // No-op
- }
-
- @Override
- public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- ApexInfo installPackage(File apexFile) {
- throw new UnsupportedOperationException("APEX updates are not supported");
- }
-
- @Override
- public List<ApexSystemServiceInfo> getApexSystemServices() {
- // TODO(satayev): we can't really support flattened apex use case, and need to migrate
- // the manifest entries into system's manifest asap.
- return Collections.emptyList();
- }
-
- @Override
- void dump(PrintWriter pw) {
- }
-
- @Override
- public File getBackingApexFile(File file) {
- return null;
- }
- }
}
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 86c4985..375ef61 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -52,7 +52,6 @@
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
-import android.sysprop.ApexProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.FastImmutableArraySet;
@@ -921,10 +920,6 @@
return rebootWithLskfImpl(packageName, reason, slotSwitch);
}
- public static boolean isUpdatableApexSupported() {
- return ApexProperties.updatable().orElse(false);
- }
-
// Metadata should be no more than few MB, if it's larger than 100MB something is wrong.
private static final long APEX_INFO_SIZE_LIMIT = 24 * 1024 * 100;
@@ -975,11 +970,6 @@
@Override
public boolean allocateSpaceForUpdate(String packageFile) {
allocateSpaceForUpdate_enforcePermission();
- if (!isUpdatableApexSupported()) {
- Log.i(TAG, "Updatable Apex not supported, "
- + "allocateSpaceForUpdate does nothing.");
- return true;
- }
final long token = Binder.clearCallingIdentity();
try {
CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5165e86..35ff3ee 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2129,8 +2129,7 @@
hasBeenLaunched = false;
mTaskSupervisor = supervisor;
- info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid,
- info.launchMode, mActivityComponent);
+ info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid);
taskAffinity = info.taskAffinity;
final String uid = Integer.toString(info.applicationInfo.uid);
if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
@@ -2230,19 +2229,12 @@
*
* @param affinity The affinity of the activity.
* @param uid The user-ID that has been assigned to this application.
- * @param launchMode The activity launch mode
- * @param componentName The activity component name. This is only useful when the given
- * launchMode is {@link ActivityInfo#LAUNCH_SINGLE_INSTANCE}
* @return The task affinity
*/
- static String computeTaskAffinity(String affinity, int uid, int launchMode,
- ComponentName componentName) {
+ static String computeTaskAffinity(String affinity, int uid) {
final String uidStr = Integer.toString(uid);
if (affinity != null && !affinity.startsWith(uidStr)) {
affinity = uidStr + ":" + affinity;
- if (launchMode == LAUNCH_SINGLE_INSTANCE && componentName != null) {
- affinity += ":" + componentName.hashCode();
- }
}
return affinity;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cbfed6c..b3a05f1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -522,7 +522,6 @@
boolean mWaitingForConfig;
// TODO(multi-display): remove some of the usages.
- @VisibleForTesting
boolean isDefaultDisplay;
/** Detect user tapping outside of current focused task bounds .*/
@@ -3358,6 +3357,7 @@
mRootWindowContainer.mTaskSupervisor
.getKeyguardController().onDisplayRemoved(mDisplayId);
mWallpaperController.resetLargestDisplay(mDisplay);
+ mWmService.mDisplayWindowSettings.onDisplayRemoved(this);
} finally {
mDisplayReady = false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 4c0435e..e1753d7 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -33,6 +33,7 @@
import android.view.DisplayInfo;
import android.view.IWindowManager;
import android.view.Surface;
+import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import com.android.server.policy.WindowManagerPolicy;
@@ -45,15 +46,20 @@
* delegates the persistence and lookup of settings values to the supplied {@link SettingsProvider}.
*/
class DisplayWindowSettings {
+ @NonNull
private final WindowManagerService mService;
+ @NonNull
private final SettingsProvider mSettingsProvider;
- DisplayWindowSettings(WindowManagerService service, SettingsProvider settingsProvider) {
+ DisplayWindowSettings(@NonNull WindowManagerService service,
+ @NonNull SettingsProvider settingsProvider) {
mService = service;
mSettingsProvider = settingsProvider;
}
- void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
+ void setUserRotation(@NonNull DisplayContent displayContent,
+ @WindowManagerPolicy.UserRotationMode int rotationMode,
+ @Surface.Rotation int rotation) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -62,7 +68,7 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setForcedSize(DisplayContent displayContent, int width, int height) {
+ void setForcedSize(@NonNull DisplayContent displayContent, int width, int height) {
if (displayContent.isDefaultDisplay) {
final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
Settings.Global.putString(mService.mContext.getContentResolver(),
@@ -77,21 +83,20 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setForcedDensity(DisplayInfo info, int density, int userId) {
+ void setForcedDensity(@NonNull DisplayInfo info, int density, int userId) {
if (info.displayId == Display.DEFAULT_DISPLAY) {
final String densityString = density == 0 ? "" : Integer.toString(density);
Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
}
- final DisplayInfo displayInfo = info;
final SettingsProvider.SettingsEntry overrideSettings =
- mSettingsProvider.getOverrideSettings(displayInfo);
+ mSettingsProvider.getOverrideSettings(info);
overrideSettings.mForcedDensity = density;
- mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ mSettingsProvider.updateOverrideSettings(info, overrideSettings);
}
- void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
+ void setForcedScalingMode(@NonNull DisplayContent displayContent, @ForceScalingMode int mode) {
if (displayContent.isDefaultDisplay) {
Settings.Global.putInt(mService.mContext.getContentResolver(),
Settings.Global.DISPLAY_SCALING_FORCE, mode);
@@ -104,7 +109,7 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setFixedToUserRotation(DisplayContent displayContent, int fixedToUserRotation) {
+ void setFixedToUserRotation(@NonNull DisplayContent displayContent, int fixedToUserRotation) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -112,8 +117,8 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setIgnoreOrientationRequest(
- DisplayContent displayContent, boolean ignoreOrientationRequest) {
+ void setIgnoreOrientationRequest(@NonNull DisplayContent displayContent,
+ boolean ignoreOrientationRequest) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -121,7 +126,9 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- private int getWindowingModeLocked(SettingsProvider.SettingsEntry settings, DisplayContent dc) {
+ @WindowConfiguration.WindowingMode
+ private int getWindowingModeLocked(@NonNull SettingsProvider.SettingsEntry settings,
+ @NonNull DisplayContent dc) {
int windowingMode = settings.mWindowingMode;
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
@@ -139,13 +146,15 @@
return windowingMode;
}
- int getWindowingModeLocked(DisplayContent dc) {
+ @WindowConfiguration.WindowingMode
+ int getWindowingModeLocked(@NonNull DisplayContent dc) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
return getWindowingModeLocked(settings, dc);
}
- void setWindowingModeLocked(DisplayContent dc, int mode) {
+ void setWindowingModeLocked(@NonNull DisplayContent dc,
+ @WindowConfiguration.WindowingMode int mode) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -157,7 +166,8 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- int getRemoveContentModeLocked(DisplayContent dc) {
+ @WindowManager.RemoveContentMode
+ int getRemoveContentModeLocked(@NonNull DisplayContent dc) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
if (settings.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
@@ -171,7 +181,8 @@
return settings.mRemoveContentMode;
}
- void setRemoveContentModeLocked(DisplayContent dc, int mode) {
+ void setRemoveContentModeLocked(@NonNull DisplayContent dc,
+ @WindowManager.RemoveContentMode int mode) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -179,14 +190,14 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) {
+ boolean shouldShowWithInsecureKeyguardLocked(@NonNull DisplayContent dc) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
return settings.mShouldShowWithInsecureKeyguard != null
? settings.mShouldShowWithInsecureKeyguard : false;
}
- void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) {
+ void setShouldShowWithInsecureKeyguardLocked(@NonNull DisplayContent dc, boolean shouldShow) {
if (!dc.isPrivate() && shouldShow) {
throw new IllegalArgumentException("Public display can't be allowed to show content"
+ " when locked");
@@ -199,7 +210,7 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) {
+ void setDontMoveToTop(@NonNull DisplayContent dc, boolean dontMoveToTop) {
DisplayInfo displayInfo = dc.getDisplayInfo();
SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getSettings(displayInfo);
@@ -207,7 +218,7 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
+ boolean shouldShowSystemDecorsLocked(@NonNull DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show system decors.
return true;
@@ -218,7 +229,7 @@
return settings.mShouldShowSystemDecors != null ? settings.mShouldShowSystemDecors : false;
}
- void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) {
+ void setShouldShowSystemDecorsLocked(@NonNull DisplayContent dc, boolean shouldShow) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -226,7 +237,8 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- @DisplayImePolicy int getImePolicyLocked(DisplayContent dc) {
+ @DisplayImePolicy
+ int getImePolicyLocked(@NonNull DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show IME.
return DISPLAY_IME_POLICY_LOCAL;
@@ -238,7 +250,7 @@
: DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
- void setDisplayImePolicy(DisplayContent dc, @DisplayImePolicy int imePolicy) {
+ void setDisplayImePolicy(@NonNull DisplayContent dc, @DisplayImePolicy int imePolicy) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
@@ -246,11 +258,11 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void applySettingsToDisplayLocked(DisplayContent dc) {
+ void applySettingsToDisplayLocked(@NonNull DisplayContent dc) {
applySettingsToDisplayLocked(dc, /* includeRotationSettings */ true);
}
- void applySettingsToDisplayLocked(DisplayContent dc, boolean includeRotationSettings) {
+ void applySettingsToDisplayLocked(@NonNull DisplayContent dc, boolean includeRotationSettings) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
@@ -274,9 +286,8 @@
dc.mIsDensityForced = hasDensityOverride;
dc.mIsSizeForced = hasSizeOverride;
- final boolean ignoreDisplayCutout = settings.mIgnoreDisplayCutout != null
+ dc.mIgnoreDisplayCutout = settings.mIgnoreDisplayCutout != null
? settings.mIgnoreDisplayCutout : false;
- dc.mIgnoreDisplayCutout = ignoreDisplayCutout;
final int width = hasSizeOverride ? settings.mForcedWidth : dc.mInitialDisplayWidth;
final int height = hasSizeOverride ? settings.mForcedHeight : dc.mInitialDisplayHeight;
@@ -296,7 +307,7 @@
if (includeRotationSettings) applyRotationSettingsToDisplayLocked(dc);
}
- void applyRotationSettingsToDisplayLocked(DisplayContent dc) {
+ void applyRotationSettingsToDisplayLocked(@NonNull DisplayContent dc) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
@@ -315,7 +326,7 @@
* @return {@code true} if any settings for this display has changed; {@code false} if nothing
* changed.
*/
- boolean updateSettingsForDisplay(DisplayContent dc) {
+ boolean updateSettingsForDisplay(@NonNull DisplayContent dc) {
final TaskDisplayArea defaultTda = dc.getDefaultTaskDisplayArea();
if (defaultTda != null && defaultTda.getWindowingMode() != getWindowingModeLocked(dc)) {
// For the time being the only thing that may change is windowing mode, so just update
@@ -327,6 +338,13 @@
}
/**
+ * Called when the given {@link DisplayContent} is removed to cleanup.
+ */
+ void onDisplayRemoved(@NonNull DisplayContent dc) {
+ mSettingsProvider.onDisplayRemoved(dc.getDisplayInfo());
+ }
+
+ /**
* Provides the functionality to lookup the {@link SettingsEntry settings} for a given
* {@link DisplayInfo}.
* <p>
@@ -364,19 +382,29 @@
void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides);
/**
+ * Called when a display is removed to cleanup.
+ */
+ void onDisplayRemoved(@NonNull DisplayInfo info);
+
+ /**
* Settings for a display.
*/
class SettingsEntry {
+ @WindowConfiguration.WindowingMode
int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@Nullable
+ @WindowManagerPolicy.UserRotationMode
Integer mUserRotationMode;
@Nullable
+ @Surface.Rotation
Integer mUserRotation;
int mForcedWidth;
int mForcedHeight;
int mForcedDensity;
@Nullable
+ @ForceScalingMode
Integer mForcedScalingMode;
+ @WindowManager.RemoveContentMode
int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED;
@Nullable
Boolean mShouldShowWithInsecureKeyguard;
@@ -395,7 +423,7 @@
SettingsEntry() {}
- SettingsEntry(SettingsEntry copyFrom) {
+ SettingsEntry(@NonNull SettingsEntry copyFrom) {
setTo(copyFrom);
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 1abb0a1..ea668fa 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
@@ -28,6 +29,8 @@
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.os.Environment;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -49,7 +52,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.HashMap;
import java.util.Map;
/**
@@ -86,7 +88,9 @@
void finishWrite(OutputStream os, boolean success);
}
+ @NonNull
private ReadableSettings mBaseSettings;
+ @NonNull
private final WritableSettings mOverrideSettings;
DisplayWindowSettingsProvider() {
@@ -155,6 +159,16 @@
mOverrideSettings.updateSettingsEntry(info, overrides);
}
+ @Override
+ public void onDisplayRemoved(@NonNull DisplayInfo info) {
+ mOverrideSettings.onDisplayRemoved(info);
+ }
+
+ @VisibleForTesting
+ int getOverrideSettingsSize() {
+ return mOverrideSettings.mSettings.size();
+ }
+
/**
* Class that allows reading {@link SettingsEntry entries} from a
* {@link ReadableSettingsStorage}.
@@ -168,14 +182,15 @@
*/
@DisplayIdentifierType
protected int mIdentifierType;
- protected final Map<String, SettingsEntry> mSettings = new HashMap<>();
+ @NonNull
+ protected final ArrayMap<String, SettingsEntry> mSettings = new ArrayMap<>();
- ReadableSettings(ReadableSettingsStorage settingsStorage) {
+ ReadableSettings(@NonNull ReadableSettingsStorage settingsStorage) {
loadSettings(settingsStorage);
}
@Nullable
- final SettingsEntry getSettingsEntry(DisplayInfo info) {
+ final SettingsEntry getSettingsEntry(@NonNull DisplayInfo info) {
final String identifier = getIdentifier(info);
SettingsEntry settings;
// Try to get corresponding settings using preferred identifier for the current config.
@@ -193,7 +208,8 @@
}
/** Gets the identifier of choice for the current config. */
- protected final String getIdentifier(DisplayInfo displayInfo) {
+ @NonNull
+ protected final String getIdentifier(@NonNull DisplayInfo displayInfo) {
if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) {
// Config suggests using port as identifier for physical displays.
if (displayInfo.address instanceof DisplayAddress.Physical) {
@@ -203,7 +219,7 @@
return displayInfo.uniqueId;
}
- private void loadSettings(ReadableSettingsStorage settingsStorage) {
+ private void loadSettings(@NonNull ReadableSettingsStorage settingsStorage) {
FileData fileData = readSettings(settingsStorage);
if (fileData != null) {
mIdentifierType = fileData.mIdentifierType;
@@ -217,15 +233,18 @@
* {@link WritableSettingsStorage}.
*/
private static final class WritableSettings extends ReadableSettings {
+ @NonNull
private final WritableSettingsStorage mSettingsStorage;
+ @NonNull
+ private final ArraySet<String> mVirtualDisplayIdentifiers = new ArraySet<>();
- WritableSettings(WritableSettingsStorage settingsStorage) {
+ WritableSettings(@NonNull WritableSettingsStorage settingsStorage) {
super(settingsStorage);
mSettingsStorage = settingsStorage;
}
@NonNull
- SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) {
+ SettingsEntry getOrCreateSettingsEntry(@NonNull DisplayInfo info) {
final String identifier = getIdentifier(info);
SettingsEntry settings;
// Try to get corresponding settings using preferred identifier for the current config.
@@ -243,21 +262,47 @@
settings = new SettingsEntry();
mSettings.put(identifier, settings);
+ if (info.type == TYPE_VIRTUAL) {
+ // Keep track of virtual display. We don't want to write virtual display settings to
+ // file.
+ mVirtualDisplayIdentifiers.add(identifier);
+ }
return settings;
}
- void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) {
+ void updateSettingsEntry(@NonNull DisplayInfo info, @NonNull SettingsEntry settings) {
final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info);
final boolean changed = overrideSettings.setTo(settings);
- if (changed) {
+ if (changed && info.type != TYPE_VIRTUAL) {
writeSettings();
}
}
+ void onDisplayRemoved(@NonNull DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ if (!mSettings.containsKey(identifier)) {
+ return;
+ }
+ if (mVirtualDisplayIdentifiers.remove(identifier)
+ || mSettings.get(identifier).isEmpty()) {
+ // Don't keep track of virtual display or empty settings to avoid growing the cached
+ // map.
+ mSettings.remove(identifier);
+ }
+ }
+
private void writeSettings() {
- FileData fileData = new FileData();
+ final FileData fileData = new FileData();
fileData.mIdentifierType = mIdentifierType;
- fileData.mSettings.putAll(mSettings);
+ final int size = mSettings.size();
+ for (int i = 0; i < size; i++) {
+ final String identifier = mSettings.keyAt(i);
+ if (mVirtualDisplayIdentifiers.contains(identifier)) {
+ // Do not write virtual display settings to file.
+ continue;
+ }
+ fileData.mSettings.put(identifier, mSettings.get(identifier));
+ }
DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData);
}
}
@@ -283,7 +328,7 @@
}
@Nullable
- private static FileData readSettings(ReadableSettingsStorage storage) {
+ private static FileData readSettings(@NonNull ReadableSettingsStorage storage) {
InputStream stream;
try {
stream = storage.openRead();
@@ -348,13 +393,14 @@
return fileData;
}
- private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) {
+ private static int getIntAttribute(@NonNull TypedXmlPullParser parser, @NonNull String name,
+ int defaultValue) {
return parser.getAttributeInt(null, name, defaultValue);
}
@Nullable
- private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name,
- @Nullable Integer defaultValue) {
+ private static Integer getIntegerAttribute(@NonNull TypedXmlPullParser parser,
+ @NonNull String name, @Nullable Integer defaultValue) {
try {
return parser.getAttributeInt(null, name);
} catch (Exception ignored) {
@@ -363,8 +409,8 @@
}
@Nullable
- private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name,
- @Nullable Boolean defaultValue) {
+ private static Boolean getBooleanAttribute(@NonNull TypedXmlPullParser parser,
+ @NonNull String name, @Nullable Boolean defaultValue) {
try {
return parser.getAttributeBoolean(null, name);
} catch (Exception ignored) {
@@ -372,7 +418,7 @@
}
}
- private static void readDisplay(TypedXmlPullParser parser, FileData fileData)
+ private static void readDisplay(@NonNull TypedXmlPullParser parser, @NonNull FileData fileData)
throws NumberFormatException, XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, "name");
if (name != null) {
@@ -420,7 +466,7 @@
XmlUtils.skipCurrentTag(parser);
}
- private static void readConfig(TypedXmlPullParser parser, FileData fileData)
+ private static void readConfig(@NonNull TypedXmlPullParser parser, @NonNull FileData fileData)
throws NumberFormatException,
XmlPullParserException, IOException {
fileData.mIdentifierType = getIntAttribute(parser, "identifier",
@@ -428,7 +474,8 @@
XmlUtils.skipCurrentTag(parser);
}
- private static void writeSettings(WritableSettingsStorage storage, FileData data) {
+ private static void writeSettings(@NonNull WritableSettingsStorage storage,
+ @NonNull FileData data) {
OutputStream stream;
try {
stream = storage.startWrite();
@@ -525,7 +572,8 @@
private static final class FileData {
int mIdentifierType;
- final Map<String, SettingsEntry> mSettings = new HashMap<>();
+ @NonNull
+ final Map<String, SettingsEntry> mSettings = new ArrayMap<>();
@Override
public String toString() {
@@ -537,6 +585,7 @@
}
private static final class AtomicFileStorage implements WritableSettingsStorage {
+ @NonNull
private final AtomicFile mAtomicFile;
AtomicFileStorage(@NonNull AtomicFile atomicFile) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 9ef5ed0..b71d918b 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -183,6 +183,8 @@
/** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */
private final ArrayList<Task> mHiddenTasks = new ArrayList<>();
+ /** The maximum size that the hidden tasks are cached. */
+ private static final int MAX_HIDDEN_TASK_SIZE = 10;
/** Whether to trim inactive tasks when activities are idle. */
private boolean mCheckTrimmableTasksOnIdle;
@@ -1477,9 +1479,13 @@
return task.compareTo(rootHomeTask) < 0;
}
- /** Remove the tasks that user may not be able to return. */
+ /** Remove the tasks that user may not be able to return when exceeds the cache limit. */
private void removeUnreachableHiddenTasks(int windowingMode) {
- for (int i = mHiddenTasks.size() - 1; i >= 0; i--) {
+ final int size = mHiddenTasks.size();
+ if (size <= MAX_HIDDEN_TASK_SIZE) {
+ return;
+ }
+ for (int i = size - 1; i >= MAX_HIDDEN_TASK_SIZE; i--) {
final Task hiddenTask = mHiddenTasks.get(i);
if (!hiddenTask.hasChild() || hiddenTask.inRecents) {
// The task was removed by other path or it became reachable (added to recents).
@@ -1523,7 +1529,7 @@
// A non-empty task is replaced by a new task. Because the removed task is no longer
// managed by the recent tasks list, add it to the hidden list to prevent the task
// from becoming dangling.
- mHiddenTasks.add(removedTask);
+ mHiddenTasks.add(0, removedTask);
}
notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
if (DEBUG_RECENTS_TRIM_TASKS) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9c23beb..92e90ae 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5406,8 +5406,7 @@
// Basic case: for simple app-centric recents, we need to recreate
// the task if the affinity has changed.
- final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(),
- srec.launchMode, srec.mActivityComponent);
+ final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid());
if (srec == null || srec.getTask().affinity == null
|| !srec.getTask().affinity.equals(affinity)) {
return true;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0248acc..358ee87 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -723,6 +723,14 @@
mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE;
return;
}
+ // Activity doesn't need to capture snapshot if the starting window has associated to task.
+ if (wc.asActivityRecord() != null) {
+ final ActivityRecord activityRecord = wc.asActivityRecord();
+ if (activityRecord.mStartingData != null
+ && activityRecord.mStartingData.mAssociatedTask != null) {
+ return;
+ }
+ }
if (mContainerFreezer == null) {
mContainerFreezer = new ScreenshotFreezer();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fa279a8..db92191 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -552,7 +552,9 @@
final PackageManagerInternal mPmInternal;
private final TestUtilityService mTestUtilityService;
+ @NonNull
final DisplayWindowSettingsProvider mDisplayWindowSettingsProvider;
+ @NonNull
final DisplayWindowSettings mDisplayWindowSettings;
/** If the system should display notifications for apps displaying an alert window. */
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f660b42..5e7dd59 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -85,7 +85,7 @@
//TODO Introduce a single lock object to lock the whole state and avoid the requirement above.
// All users should be able to connect to USB and Bluetooth MIDI devices.
-// All users can create can install an app that provides, a Virtual MIDI Device Service.
+// All users can create can install an app that provides a Virtual MIDI Device Service.
// Users can not open virtual MIDI devices created by other users.
// getDevices() surfaces devices that can be opened by that user.
// openDevice() rejects devices that are cannot be opened by that user.
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 292320e..885ed35 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -63,9 +63,8 @@
private static final String TAG = "PeopleService";
- private DataManager mDataManager;
- @VisibleForTesting
- ConversationListenerHelper mConversationListenerHelper;
+ private DataManager mLazyDataManager;
+ private ConversationListenerHelper mLazyConversationListenerHelper;
private PackageManagerInternal mPackageManagerInternal;
@@ -76,17 +75,30 @@
*/
public PeopleService(Context context) {
super(context);
-
- mDataManager = new DataManager(context);
- mConversationListenerHelper = new ConversationListenerHelper();
- mDataManager.addConversationsListener(mConversationListenerHelper);
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- mDataManager.initialize();
+ @VisibleForTesting
+ ConversationListenerHelper getConversationListenerHelper() {
+ if (mLazyConversationListenerHelper == null) {
+ initLazyStuff();
}
+ return mLazyConversationListenerHelper;
+ }
+
+ private synchronized void initLazyStuff() {
+ if (mLazyDataManager == null) {
+ mLazyDataManager = new DataManager(getContext());
+ mLazyDataManager.initialize();
+ mLazyConversationListenerHelper = new ConversationListenerHelper();
+ mLazyDataManager.addConversationsListener(mLazyConversationListenerHelper);
+ }
+ }
+
+ private DataManager getDataManager() {
+ if (mLazyDataManager == null) {
+ initLazyStuff();
+ }
+ return mLazyDataManager;
}
@Override
@@ -105,12 +117,12 @@
@Override
public void onUserUnlocked(@NonNull TargetUser user) {
- mDataManager.onUserUnlocked(user.getUserIdentifier());
+ getDataManager().onUserUnlocked(user.getUserIdentifier());
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
- mDataManager.onUserStopping(user.getUserIdentifier());
+ getDataManager().onUserStopping(user.getUserIdentifier());
}
/**
@@ -171,28 +183,28 @@
public ConversationChannel getConversation(
String packageName, int userId, String shortcutId) {
enforceSystemRootOrSystemUI(getContext(), "get conversation");
- return mDataManager.getConversation(packageName, userId, shortcutId);
+ return getDataManager().getConversation(packageName, userId, shortcutId);
}
@Override
public ParceledListSlice<ConversationChannel> getRecentConversations() {
enforceSystemRootOrSystemUI(getContext(), "get recent conversations");
return new ParceledListSlice<>(
- mDataManager.getRecentConversations(
+ getDataManager().getRecentConversations(
Binder.getCallingUserHandle().getIdentifier()));
}
@Override
public void removeRecentConversation(String packageName, int userId, String shortcutId) {
enforceSystemOrRoot("remove a recent conversation");
- mDataManager.removeRecentConversation(packageName, userId, shortcutId,
+ getDataManager().removeRecentConversation(packageName, userId, shortcutId,
Binder.getCallingUserHandle().getIdentifier());
}
@Override
public void removeAllRecentConversations() {
enforceSystemOrRoot("remove all recent conversations");
- mDataManager.removeAllRecentConversations(
+ getDataManager().removeAllRecentConversations(
Binder.getCallingUserHandle().getIdentifier());
}
@@ -200,7 +212,7 @@
public boolean isConversation(String packageName, int userId, String shortcutId) {
enforceHasReadPeopleDataPermission();
handleIncomingUser(userId);
- return mDataManager.isConversation(packageName, userId, shortcutId);
+ return getDataManager().isConversation(packageName, userId, shortcutId);
}
private void enforceHasReadPeopleDataPermission() throws SecurityException {
@@ -213,7 +225,7 @@
@Override
public long getLastInteraction(String packageName, int userId, String shortcutId) {
enforceSystemRootOrSystemUI(getContext(), "get last interaction");
- return mDataManager.getLastInteraction(packageName, userId, shortcutId);
+ return getDataManager().getLastInteraction(packageName, userId, shortcutId);
}
@Override
@@ -224,7 +236,7 @@
if (status.getStartTimeMillis() > System.currentTimeMillis()) {
throw new IllegalArgumentException("Start time must be in the past");
}
- mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status);
+ getDataManager().addOrUpdateStatus(packageName, userId, conversationId, status);
}
@Override
@@ -232,14 +244,14 @@
String statusId) {
handleIncomingUser(userId);
checkCallerIsSameApp(packageName);
- mDataManager.clearStatus(packageName, userId, conversationId, statusId);
+ getDataManager().clearStatus(packageName, userId, conversationId, statusId);
}
@Override
public void clearStatuses(String packageName, int userId, String conversationId) {
handleIncomingUser(userId);
checkCallerIsSameApp(packageName);
- mDataManager.clearStatuses(packageName, userId, conversationId);
+ getDataManager().clearStatuses(packageName, userId, conversationId);
}
@Override
@@ -250,21 +262,21 @@
checkCallerIsSameApp(packageName);
}
return new ParceledListSlice<>(
- mDataManager.getStatuses(packageName, userId, conversationId));
+ getDataManager().getStatuses(packageName, userId, conversationId));
}
@Override
public void registerConversationListener(
String packageName, int userId, String shortcutId, IConversationListener listener) {
enforceSystemRootOrSystemUI(getContext(), "register conversation listener");
- mConversationListenerHelper.addConversationListener(
+ getConversationListenerHelper().addConversationListener(
new ListenerKey(packageName, userId, shortcutId), listener);
}
@Override
public void unregisterConversationListener(IConversationListener listener) {
enforceSystemRootOrSystemUI(getContext(), "unregister conversation listener");
- mConversationListenerHelper.removeConversationListener(listener);
+ getConversationListenerHelper().removeConversationListener(listener);
}
};
@@ -393,7 +405,7 @@
public void onCreatePredictionSession(AppPredictionContext appPredictionContext,
AppPredictionSessionId sessionId) {
mSessions.put(sessionId,
- new SessionInfo(appPredictionContext, mDataManager, sessionId.getUserId(),
+ new SessionInfo(appPredictionContext, getDataManager(), sessionId.getUserId(),
getContext()));
}
@@ -448,18 +460,18 @@
@Override
public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
- mDataManager.pruneDataForUser(userId, signal);
+ getDataManager().pruneDataForUser(userId, signal);
}
@Nullable
@Override
public byte[] getBackupPayload(@UserIdInt int userId) {
- return mDataManager.getBackupPayload(userId);
+ return getDataManager().getBackupPayload(userId);
}
@Override
public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
- mDataManager.restore(userId, payload);
+ getDataManager().restore(userId, payload);
}
@VisibleForTesting
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 5b0e2f3..1ae9124 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -474,15 +474,6 @@
}
@Test
- public void testGetBackingApexFiles_flattenedApex() {
- ApexManager flattenedApexManager = new ApexManager.ApexManagerFlattenedApex();
- final File backingApexFile = flattenedApexManager.getBackingApexFile(
- new File(mMockSystem.system().getApexDirectory(),
- "com.android.apex.cts.shim/app/CtsShim/CtsShim.apk"));
- assertThat(backingApexFile).isNull();
- }
-
- @Test
public void testActiveApexChanged() throws RemoteException {
ApexInfo apex1 = createApexInfo(
"com.apex1", 37, true, true, new File("/data/apex/active/com.apex@37.apex"));
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index 5066240..aed8491 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -41,8 +41,10 @@
import android.app.prediction.AppTarget;
import android.app.prediction.IPredictionCallback;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -56,6 +58,7 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -87,6 +90,13 @@
private AppPredictionContext mPredictionContext;
@Mock
+ ShortcutServiceInternal mShortcutServiceInternal;
+ @Mock
+ PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ NotificationManagerInternal mNotificationManagerInternal;
+
+ @Mock
private Context mMockContext;
@Rule
@@ -110,6 +120,10 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ LocalServices.addService(ShortcutServiceInternal.class, mShortcutServiceInternal);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+
mPeopleService = new TestablePeopleService(mContext);
mTestableLooper = TestableLooper.get(this);
mIPeopleManager = ((IPeopleManager) mPeopleService.mService);
@@ -137,6 +151,9 @@
@After
public void tearDown() {
LocalServices.removeServiceForTest(PeopleServiceInternal.class);
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(NotificationManagerInternal.class);
}
@Test
@@ -167,25 +184,29 @@
@Test
public void testRegisterConversationListener() throws Exception {
assertEquals(0,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
new TestableConversationListener());
mTestableLooper.processAllMessages();
assertEquals(1,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
new TestableConversationListener());
mTestableLooper.processAllMessages();
assertEquals(2,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_2,
new TestableConversationListener());
mTestableLooper.processAllMessages();
assertEquals(3,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
}
@Test
@@ -201,20 +222,24 @@
listener3);
mTestableLooper.processAllMessages();
assertEquals(3,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.unregisterConversationListener(
listener2);
assertEquals(2,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.unregisterConversationListener(
listener1);
assertEquals(1,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
mIPeopleManager.unregisterConversationListener(
listener3);
assertEquals(0,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
}
@Test
@@ -229,12 +254,13 @@
PeopleManager.ConversationListener.class);
registerListener(CONVERSATION_ID_2, listenerForConversation2);
assertEquals(3,
- mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mPeopleService.getConversationListenerHelper()
+ .mListeners.getRegisteredCallbackCount());
// Update conversation with two listeners.
ConversationStatus status = new ConversationStatus.Builder(CONVERSATION_ID_1,
ACTIVITY_GAME).build();
- mPeopleService.mConversationListenerHelper.onConversationsUpdate(
+ mPeopleService.getConversationListenerHelper().onConversationsUpdate(
Arrays.asList(getConversation(CONVERSATION_ID_1, status)));
mTestLooper.dispatchAll();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 870dd48..7284744 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1749,8 +1749,7 @@
public void testLaunchActivityWithoutDisplayCategory() {
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
- info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */, null /* componentName */);
+ info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
@@ -1775,8 +1774,7 @@
public void testLaunchActivityWithDifferentDisplayCategory() {
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
- info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */, null /* componentName */);
+ info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
@@ -1801,8 +1799,7 @@
public void testLaunchActivityWithSameDisplayCategory() {
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
- info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
- 0 /* launchMode */, null /* componentName */);
+ info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID);
info.requiredDisplayCategory = "automotive";
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 9d839fc..7d9fdd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -26,6 +27,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
@@ -233,6 +235,22 @@
}
@Test
+ public void testDoNotWriteVirtualDisplaySettingsToStorage() throws Exception {
+ final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
+ secondaryDisplayInfo.type = TYPE_VIRTUAL;
+
+ // No write to storage on virtual display change.
+ final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+ final SettingsEntry virtualSettings = provider.getOverrideSettings(secondaryDisplayInfo);
+ virtualSettings.mShouldShowSystemDecors = true;
+ virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+ virtualSettings.mDontMoveToTop = true;
+ provider.updateOverrideSettings(secondaryDisplayInfo, virtualSettings);
+ assertFalse(mOverrideSettingsStorage.wasWriteSuccessful());
+ }
+
+ @Test
public void testWritingDisplaySettingsToStorage_UsePortAsId() throws Exception {
prepareOverrideDisplaySettings(null /* displayIdentifier */, true /* usePortAsId */);
@@ -260,6 +278,54 @@
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy"));
}
+ @Test
+ public void testCleanUpEmptyDisplaySettingsOnDisplayRemoved() {
+ final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+ final int initialSize = provider.getOverrideSettingsSize();
+
+ // Size + 1 when query for a new display.
+ final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
+ final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
+
+ assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
+
+ // When a display is removed, its override Settings is not removed if there is any override.
+ overrideSettings.mShouldShowSystemDecors = true;
+ provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+ provider.onDisplayRemoved(secondaryDisplayInfo);
+
+ assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
+
+ // When a display is removed, its override Settings is removed if there is no override.
+ provider.updateOverrideSettings(secondaryDisplayInfo, new SettingsEntry());
+ provider.onDisplayRemoved(secondaryDisplayInfo);
+
+ assertEquals(initialSize, provider.getOverrideSettingsSize());
+ }
+
+ @Test
+ public void testCleanUpVirtualDisplaySettingsOnDisplayRemoved() {
+ final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+ final int initialSize = provider.getOverrideSettingsSize();
+
+ // Size + 1 when query for a new display.
+ final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
+ secondaryDisplayInfo.type = TYPE_VIRTUAL;
+ final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
+
+ assertEquals(initialSize + 1, provider.getOverrideSettingsSize());
+
+ // When a virtual display is removed, its override Settings is removed even if it has
+ // override.
+ overrideSettings.mShouldShowSystemDecors = true;
+ provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
+ provider.onDisplayRemoved(secondaryDisplayInfo);
+
+ assertEquals(initialSize, provider.getOverrideSettingsSize());
+ }
+
/**
* Prepares display settings and stores in {@link #mOverrideSettingsStorage}. Uses provided
* display identifier and stores windowingMode=WINDOWING_MODE_PINNED.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 1cec0ef..e54b8e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -484,6 +484,18 @@
assertTrue(dcIgnoreOrientation.getIgnoreOrientationRequest());
}
+ @Test
+ public void testDisplayRemoval() {
+ spyOn(mWm.mDisplayWindowSettings);
+ spyOn(mWm.mDisplayWindowSettingsProvider);
+
+ mPrivateDisplay.removeImmediately();
+
+ verify(mWm.mDisplayWindowSettings).onDisplayRemoved(mPrivateDisplay);
+ verify(mWm.mDisplayWindowSettingsProvider).onDisplayRemoved(
+ mPrivateDisplay.getDisplayInfo());
+ }
+
public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
@@ -513,5 +525,10 @@
overrideSettings.setTo(settings);
}
+
+ @Override
+ public void onDisplayRemoved(@NonNull DisplayInfo info) {
+ mOverrideSettingsCache.remove(info);
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b02b774..f23e56d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -27,8 +27,6 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
@@ -451,25 +449,15 @@
final int uid = 10123;
final Task task1 = createTaskBuilder(".Task1").build();
final ComponentName componentName = getUniqueComponentName();
- task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
- componentName);
+ task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid);
mRecentTasks.add(task1);
// Add another task to recents, and make sure the previous task was removed.
final Task task2 = createTaskBuilder(".Task2").build();
- task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
- componentName);
+ task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid);
mRecentTasks.add(task2);
assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
-
- // Add another single-instance task to recents, and make sure no task is removed.
- final Task task3 = createTaskBuilder(".Task3").build();
- task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid,
- LAUNCH_SINGLE_INSTANCE, componentName);
- mRecentTasks.add(task3);
- assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
- true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java
index b2e44b1..e11df98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java
@@ -52,6 +52,12 @@
overrideSettings.setTo(overrides);
}
+ @Override
+ public void onDisplayRemoved(@NonNull DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ mOverrideSettingsMap.remove(identifier);
+ }
+
@NonNull
private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) {
final String identifier = getIdentifier(info);
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index a2fbc95..882c277 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -772,6 +772,18 @@
}
}
+ /**
+ * Convert isNonTerrestrialNetwork to string
+ *
+ * @param isNonTerrestrialNetwork boolean indicating whether network is a non-terrestrial
+ * network
+ * @return string format of isNonTerrestrialNetwork.
+ * @hide
+ */
+ public static String isNonTerrestrialNetworkToString(boolean isNonTerrestrialNetwork) {
+ return isNonTerrestrialNetwork ? "NON-TERRESTRIAL" : "TERRESTRIAL";
+ }
+
@NonNull
@Override
public String toString() {
@@ -797,7 +809,8 @@
? nrStateToString(mNrState) : "****")
.append(" rRplmn=").append(mRplmn)
.append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
- .append(" isNonTerrestrialNetwork=").append(mIsNonTerrestrialNetwork)
+ .append(" isNonTerrestrialNetwork=").append(
+ isNonTerrestrialNetworkToString(mIsNonTerrestrialNetwork))
.append("}").toString();
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index b18a1a8..4d36111 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -19,6 +19,8 @@
package com.android.server.wm.flicker
import android.tools.common.PlatformConsts
+import android.tools.common.Position
+import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.flicker.subject.region.RegionSubject
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentNameMatcher
@@ -169,12 +171,7 @@
*/
fun LegacyFlickerTest.navBarLayerPositionAtStart() {
assertLayersStart {
- val display =
- this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!")
- this.visibleRegion(ComponentNameMatcher.NAV_BAR)
- .coversExactly(
- WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
- )
+ assertNavBarPosition(this, scenario.isGesturalNavigation)
}
}
@@ -184,13 +181,36 @@
*/
fun LegacyFlickerTest.navBarLayerPositionAtEnd() {
assertLayersEnd {
- val display =
- this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
- this.visibleRegion(ComponentNameMatcher.NAV_BAR)
- .coversExactly(
- WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
- )
+ assertNavBarPosition(this, scenario.isGesturalNavigation)
+ }
+}
+
+private fun assertNavBarPosition(sfState: LayerTraceEntrySubject, isGesturalNavigation: Boolean) {
+ val display =
+ sfState.entry.displays.filterNot { it.isOff }.minByOrNull { it.id }
+ ?: error("There is no display!")
+ val displayArea = display.layerStackSpace
+ val navBarPosition = display.navBarPosition(isGesturalNavigation)
+ val navBarRegion = sfState.visibleRegion(ComponentNameMatcher.NAV_BAR)
+
+ when (navBarPosition) {
+ Position.TOP ->
+ navBarRegion.hasSameTopPosition(displayArea)
+ .hasSameLeftPosition(displayArea)
+ .hasSameRightPosition(displayArea)
+ Position.BOTTOM ->
+ navBarRegion.hasSameBottomPosition(displayArea)
+ .hasSameLeftPosition(displayArea)
+ .hasSameRightPosition(displayArea)
+ Position.LEFT ->
+ navBarRegion.hasSameLeftPosition(displayArea)
+ .hasSameTopPosition(displayArea)
+ .hasSameBottomPosition(displayArea)
+ Position.RIGHT ->
+ navBarRegion.hasSameRightPosition(displayArea)
+ .hasSameTopPosition(displayArea)
+ .hasSameBottomPosition(displayArea)
+ else -> error("Unknown position $navBarPosition")
}
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 0d6dc35..7323b0f 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -47,6 +47,7 @@
"-Wno-missing-field-initializers",
"-fno-exceptions",
"-fno-rtti",
+ "-Wno-deprecated-declarations",
],
target: {
windows: {
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 4cd7eae..27c354a 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -40,10 +40,8 @@
};
// Info about an APK loaded in memory.
-class LoadedApk {
+class LoadedApk final {
public:
- virtual ~LoadedApk() = default;
-
// Loads both binary and proto APKs from disk.
static std::unique_ptr<LoadedApk> LoadApkFromPath(android::StringPiece path,
android::IDiagnostics* diag);
@@ -96,8 +94,8 @@
* Writes the APK on disk at the given path, while also removing the resource
* files that are not referenced in the resource table.
*/
- virtual bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
- IArchiveWriter* writer);
+ bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
+ IArchiveWriter* writer);
/**
* Writes the APK on disk at the given path, while also removing the resource files that are not
@@ -108,9 +106,9 @@
* original manifest will be written. The manifest is only required if the contents of the new APK
* have been modified in a way that require the AndroidManifest.xml to also be modified.
*/
- virtual bool WriteToArchive(IAaptContext* context, ResourceTable* split_table,
- const TableFlattenerOptions& options, FilterChain* filters,
- IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
+ bool WriteToArchive(IAaptContext* context, ResourceTable* split_table,
+ const TableFlattenerOptions& options, FilterChain* filters,
+ IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
/** Loads the file as an xml document. */
std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path,
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index a766bd4..83f2eb3 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -16,20 +16,24 @@
#include "SdkConstants.h"
+#include <stdint.h>
+
#include <algorithm>
#include <string>
-#include <unordered_set>
-#include <vector>
+#include <string_view>
using android::StringPiece;
+using namespace std::literals;
namespace aapt {
-static ApiVersion sDevelopmentSdkLevel = 10000;
-static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>(
- {"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream"});
+static constexpr ApiVersion sDevelopmentSdkLevel = 10000;
+static constexpr StringPiece sDevelopmentSdkCodeNames[] = {
+ "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv};
-static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
+static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv;
+
+static constexpr std::pair<uint16_t, ApiVersion> sAttrIdMap[] = {
{0x021c, 1},
{0x021d, 2},
{0x0269, SDK_CUPCAKE},
@@ -62,25 +66,37 @@
{0x064c, SDK_S_V2},
};
-static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
- return p.first < entryId;
-}
+static_assert(std::is_sorted(std::begin(sAttrIdMap), std::end(sAttrIdMap),
+ [](auto&& l, auto&& r) { return l.first < r.first; }));
ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
if (id.package_id() != 0x01 || id.type_id() != 0x01) {
return 0;
}
- auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entry_id(), less_entry_id);
- if (iter == sAttrIdMap.end()) {
+ const auto it =
+ std::lower_bound(std::begin(sAttrIdMap), std::end(sAttrIdMap), id.entry_id(),
+ [](const auto& pair, uint16_t entryId) { return pair.first < entryId; });
+ if (it == std::end(sAttrIdMap)) {
return SDK_LOLLIPOP_MR1;
}
- return iter->second;
+ return it->second;
}
std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(StringPiece code_name) {
- return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
- ? std::optional<ApiVersion>()
- : sDevelopmentSdkLevel;
+ const auto it =
+ std::find_if(std::begin(sDevelopmentSdkCodeNames), std::end(sDevelopmentSdkCodeNames),
+ [code_name](const auto& item) { return code_name.starts_with(item); });
+ if (it == std::end(sDevelopmentSdkCodeNames)) {
+ return {};
+ }
+ if (code_name.size() == it->size()) {
+ return sDevelopmentSdkLevel;
+ }
+ if (code_name.size() == it->size() + sPrivacySandboxSuffix.size() &&
+ code_name.ends_with(sPrivacySandboxSuffix)) {
+ return sDevelopmentSdkLevel;
+ }
+ return {};
}
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants_test.cpp b/tools/aapt2/SdkConstants_test.cpp
index 61f4d71..0f645ff 100644
--- a/tools/aapt2/SdkConstants_test.cpp
+++ b/tools/aapt2/SdkConstants_test.cpp
@@ -28,4 +28,24 @@
EXPECT_EQ(0, FindAttributeSdkLevel(ResourceId(0x7f010345)));
}
+TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionValid) {
+ EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("Q"));
+ EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("VanillaIceCream"));
+}
+
+TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionPrivacySandbox) {
+ EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("QPrivacySandbox"));
+ EXPECT_EQ(std::optional<ApiVersion>(10000),
+ GetDevelopmentSdkCodeNameVersion("VanillaIceCreamPrivacySandbox"));
+}
+
+TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionInvalid) {
+ EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("A"));
+ EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("Sv3"));
+ EXPECT_EQ(std::optional<ApiVersion>(),
+ GetDevelopmentSdkCodeNameVersion("VanillaIceCream_PrivacySandbox"));
+ EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("PrivacySandbox"));
+ EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("QQQQQQQQQQQQQQQ"));
+}
+
} // namespace aapt